home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / system-config-printer / system-config-printer.py < prev    next >
Encoding:
Python Source  |  2009-05-05  |  268.0 KB  |  6,758 lines

  1. #!/usr/bin/env python
  2.  
  3. ## system-config-printer
  4.  
  5. ## Copyright (C) 2006, 2007, 2008, 2009 Red Hat, Inc.
  6. ## Copyright (C) 2006, 2007, 2008, 2009 Tim Waugh <twaugh@redhat.com>
  7. ## Copyright (C) 2006, 2007 Florian Festi <ffesti@redhat.com>
  8.  
  9. ## This program is free software; you can redistribute it and/or modify
  10. ## it under the terms of the GNU General Public License as published by
  11. ## the Free Software Foundation; either version 2 of the License, or
  12. ## (at your option) any later version.
  13.  
  14. ## This program is distributed in the hope that it will be useful,
  15. ## but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. ## GNU General Public License for more details.
  18.  
  19. ## You should have received a copy of the GNU General Public License
  20. ## along with this program; if not, write to the Free Software
  21. ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  
  23. # config is generated from config.py.in by configure
  24. import config
  25.  
  26. import errno
  27. import sys, os, tempfile, time, traceback, re, httplib, glob
  28. import subprocess
  29. import signal, thread
  30. import dbus
  31. try:
  32.     import gtk.glade
  33. except RuntimeError, e:
  34.     print "system-config-printer:", e
  35.     print "This is a graphical application and requires DISPLAY to be set."
  36.     sys.exit (1)
  37.  
  38. import gnome
  39. gtk.about_dialog_set_url_hook (lambda x, y: gnome.url_show (y))
  40. gtk.about_dialog_set_email_hook (lambda x, y: gnome.url_show ("mailto:" + y))
  41.  
  42. def show_help():
  43.     print ("\nThis is system-config-printer, " \
  44.            "a CUPS server configuration program.\n\n"
  45.            "Options:\n\n"
  46.            "  --setup-printer URI\n"
  47.            "            Select the (detected) CUPS device URI on start-up,\n"
  48.            "            and run the new-printer wizard for it.\n\n"
  49.            "  --configure-printer NAME\n"
  50.            "            Select the named printer on start-up, and open its\n"
  51.            "            properties dialog.\n\n"
  52.            "  --choose-driver NAME\n"
  53.            "            Select the named printer on start-up, and display\n"
  54.            "            the list of drivers.\n\n"
  55.            "  --print-test-page NAME\n"
  56.            "            Select the named printer on start-up and print a\n"
  57.            "            test page to it.\n\n"
  58.            "  --no-focus-on-map\n"
  59.            "            Do not focus the main window, to prevent focus \n"
  60.            "            stealing\n\n"
  61.            "  --debug   Enable debugging output.\n")
  62.  
  63. if len(sys.argv)>1 and sys.argv[1] == '--help':
  64.     show_help ()
  65.     sys.exit (0)
  66.  
  67. import cups
  68. cups.require ("1.9.42")
  69.  
  70. try:
  71.     import pysmb
  72.     PYSMB_AVAILABLE=True
  73. except:
  74.     PYSMB_AVAILABLE=False
  75.  
  76. import cupshelpers, options
  77. import gobject # for TYPE_STRING and TYPE_PYOBJECT
  78. from glade import GtkGUI
  79. from optionwidgets import OptionWidget
  80. from debug import *
  81. import probe_printer
  82. import gtk_label_autowrap
  83. from gtk_treeviewtooltips import TreeViewTooltips
  84. import urllib
  85. import troubleshoot
  86. import jobviewer
  87. import authconn
  88. import monitor
  89. from smburi import SMBURI
  90. import errordialogs
  91. from errordialogs import *
  92. import installpackage
  93. import userdefault
  94. from AdvancedServerSettings import AdvancedServerSettingsDialog
  95. from PhysicalDevice import PhysicalDevice
  96. from ToolbarSearchEntry import *
  97. from GroupsPane import *
  98. from GroupsPaneModel import *
  99. from SearchCriterion import *
  100. import gtkinklevel
  101. import pango
  102. import statereason
  103.  
  104. domain='system-config-printer'
  105. import locale
  106. try:
  107.     locale.setlocale (locale.LC_ALL, "")
  108. except locale.Error:
  109.     os.environ['LC_ALL'] = 'C'
  110.     locale.setlocale (locale.LC_ALL, "")
  111. from gettext import gettext as _
  112. monitor.set_gettext_function (_)
  113. errordialogs.set_gettext_function (_)
  114. authconn.set_gettext_function (_)
  115. import gettext
  116. gettext.textdomain (domain)
  117. gettext.bindtextdomain (domain, config.localedir)
  118. gtk.glade.textdomain (domain)
  119. gtk.glade.bindtextdomain (domain, config.localedir)
  120. import ppdippstr
  121. pkgdata = config.pkgdatadir
  122. iconpath = os.path.join (pkgdata, 'icons/')
  123. sys.path.append (pkgdata)
  124.  
  125. busy_cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
  126. ready_cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)
  127.  
  128. TEXT_start_firewall_tool = _("To do this, select "
  129.                              "System->Administration->Firewall "
  130.                              "from the main menu.")
  131.  
  132. try:
  133.     try_CUPS_SERVER_REMOTE_ANY = cups.CUPS_SERVER_REMOTE_ANY
  134. except AttributeError:
  135.     # cups module was compiled with CUPS < 1.3
  136.     try_CUPS_SERVER_REMOTE_ANY = "_remote_any"
  137.  
  138. def validDeviceURI (uri):
  139.     """Returns True is the provided URI is valid."""
  140.     (scheme, rest) = urllib.splittype (uri)
  141.     if scheme == None or scheme == '':
  142.         return False
  143.     if rest == None or rest.strip ('/') == '':
  144.         return False
  145.     return True
  146.  
  147. def CUPS_server_hostname ():
  148.     host = cups.getServer ()
  149.     if host[0] == '/':
  150.         return 'localhost'
  151.     return host
  152.  
  153. # Both the printer properties window and the new printer window
  154. # need to be able to drive 'class members' selections.
  155. def moveClassMembers(treeview_from, treeview_to):
  156.     selection = treeview_from.get_selection()
  157.     model_from, rows = selection.get_selected_rows()
  158.     rows = [gtk.TreeRowReference(model_from, row) for row in rows]
  159.  
  160.     model_to = treeview_to.get_model()
  161.  
  162.     for row in rows:
  163.         path = row.get_path()
  164.         iter = model_from.get_iter(path)
  165.  
  166.         row_data = model_from.get(iter, 0)
  167.         model_to.append(row_data)
  168.         model_from.remove(iter)
  169.  
  170. def getCurrentClassMembers(treeview):
  171.     model = treeview.get_model()
  172.     iter = model.get_iter_root()
  173.     result = []
  174.     while iter:
  175.         result.append(model.get(iter, 0)[0])
  176.         iter = model.iter_next(iter)
  177.     result.sort()
  178.     return result
  179.  
  180. def on_delete_just_hide (widget, event):
  181.     widget.hide ()
  182.     return True # stop other handlers
  183.  
  184. class GUI(GtkGUI, monitor.Watcher):
  185.  
  186.     printer_states = { cups.IPP_PRINTER_IDLE: _("Idle"),
  187.                        cups.IPP_PRINTER_PROCESSING: _("Processing"),
  188.                        cups.IPP_PRINTER_BUSY: _("Busy"),
  189.                        cups.IPP_PRINTER_STOPPED: _("Stopped") }
  190.  
  191.     def __init__(self, setup_printer = None, configure_printer = None,
  192.                  change_ppd = False, devid = "", print_test_page = False,
  193.                  focus_on_map = True):
  194.  
  195.         try:
  196.             self.language = locale.getlocale(locale.LC_MESSAGES)
  197.             self.encoding = locale.getlocale(locale.LC_CTYPE)
  198.         except:
  199.             nonfatalException()
  200.             os.environ['LC_ALL'] = 'C'
  201.             locale.setlocale (locale.LC_ALL, "")
  202.             self.language = locale.getlocale(locale.LC_MESSAGES)
  203.             self.encoding = locale.getlocale(locale.LC_CTYPE)
  204.  
  205.         self.printer = None
  206.         self.conflicts = set() # of options
  207.         self.connect_server = (self.printer and self.printer.getServer()) \
  208.                                or cups.getServer()
  209.         self.connect_encrypt = cups.getEncryption ()
  210.         self.connect_user = cups.getUser()
  211.  
  212.         self.changed = set() # of options
  213.  
  214.         self.servers = set((self.connect_server,))
  215.         self.server_is_publishing = False
  216.         self.devid = devid
  217.         self.focus_on_map = focus_on_map
  218.  
  219.         # WIDGETS
  220.         # =======
  221.         self.updating_widgets = False
  222.         self.getWidgets({"PrintersWindow":
  223.                              ["PrintersWindow",
  224.                               "view_area_vbox",
  225.                               "view_area_scrolledwindow",
  226.                               "dests_iconview",
  227.                               "statusbarMain",
  228.                               "toolbar",
  229.                               "server_settings_menu_entry",
  230.                               "new_printer",
  231.                               "new_class",
  232.                               "group_menubar_item",
  233.                               "printer_menubar_item",
  234.                               "view_discovered_printers",
  235.                               "view_groups"],
  236.                          "AboutDialog":
  237.                              ["AboutDialog"],
  238.                          "WaitWindow":
  239.                              ["WaitWindow",
  240.                               "lblWait"],
  241.                          "ConnectDialog":
  242.                              ["ConnectDialog",
  243.                               "chkEncrypted",
  244.                               "cmbServername",
  245.                               "btnConnect"],
  246.                          "ConnectingDialog":
  247.                              ["ConnectingDialog",
  248.                               "lblConnecting",
  249.                               "pbarConnecting"],
  250.                          "NewPrinterName":
  251.                              ["NewPrinterName",
  252.                               "entCopyName",
  253.                               "btnCopyOk"],
  254.                          "ServerSettingsDialog":
  255.                              ["ServerSettingsDialog",
  256.                               "chkServerBrowse",
  257.                               "chkServerShare",
  258.                               "chkServerShareAny",
  259.                               "chkServerRemoteAdmin",
  260.                               "chkServerAllowCancelAll",
  261.                               "chkServerLogDebug",
  262.                               "hboxServerBrowse"],
  263.                          "PrinterPropertiesDialog":
  264.                              ["PrinterPropertiesDialog",
  265.                               "tvPrinterProperties",
  266.                               "btnPrinterPropertiesCancel",
  267.                               "btnPrinterPropertiesOK",
  268.                               "btnPrinterPropertiesApply",
  269.                               "btnPrinterPropertiesClose",
  270.                               "ntbkPrinter",
  271.                               "entPDescription",
  272.                               "entPLocation",
  273.                               "lblPMakeModel",
  274.                               "lblPMakeModel2",
  275.                               "lblPState",
  276.                               "entPDevice",
  277.                               "lblPDevice2",
  278.                               "btnSelectDevice",
  279.                               "btnChangePPD",
  280.                               "chkPEnabled",
  281.                               "chkPAccepting",
  282.                               "chkPShared",
  283.                               "lblNotPublished",
  284.                               "btnPrintTestPage",
  285.                               "btnSelfTest",
  286.                               "btnCleanHeads",
  287.                               "btnConflict",
  288.  
  289.                               "cmbPStartBanner",
  290.                               "cmbPEndBanner",
  291.                               "cmbPErrorPolicy",
  292.                               "cmbPOperationPolicy",
  293.  
  294.                               "rbtnPAllow",
  295.                               "rbtnPDeny",
  296.                               "tvPUsers",
  297.                               "entPUser",
  298.                               "btnPAddUser",
  299.                               "btnPDelUser",
  300.  
  301.                               "lblPInstallOptions",
  302.                               "swPInstallOptions",
  303.                               "vbPInstallOptions",
  304.                               "swPOptions",
  305.                               "lblPOptions",
  306.                               "vbPOptions",
  307.                               "algnClassMembers",
  308.                               "vbClassMembers",
  309.                               "lblClassMembers",
  310.                               "tvClassMembers",
  311.                               "tvClassNotMembers",
  312.                               "btnClassAddMember",
  313.                               "btnClassDelMember",
  314.                               "btnRefreshMarkerLevels",
  315.                               "tvPrinterStateReasons",
  316.                               "ntbkPrinterStateReasons",
  317.  
  318.                               # Job options
  319.                               "sbJOCopies", "btnJOResetCopies",
  320.                               "cmbJOOrientationRequested", "btnJOResetOrientationRequested",
  321.                               "cbJOFitplot", "btnJOResetFitplot",
  322.                               "cmbJONumberUp", "btnJOResetNumberUp",
  323.                               "cmbJONumberUpLayout", "btnJOResetNumberUpLayout",
  324.                               "sbJOBrightness", "btnJOResetBrightness",
  325.                               "cmbJOFinishings", "btnJOResetFinishings",
  326.                               "sbJOJobPriority", "btnJOResetJobPriority",
  327.                               "cmbJOMedia", "btnJOResetMedia",
  328.                               "cmbJOSides", "btnJOResetSides",
  329.                               "cmbJOHoldUntil", "btnJOResetHoldUntil",
  330.                               "cbJOMirror", "btnJOResetMirror",
  331.                               "sbJOScaling", "btnJOResetScaling",
  332.                               "sbJOSaturation", "btnJOResetSaturation",
  333.                               "sbJOHue", "btnJOResetHue",
  334.                               "sbJOGamma", "btnJOResetGamma",
  335.                               "sbJOCpi", "btnJOResetCpi",
  336.                               "sbJOLpi", "btnJOResetLpi",
  337.                               "sbJOPageLeft", "btnJOResetPageLeft",
  338.                               "sbJOPageRight", "btnJOResetPageRight",
  339.                               "sbJOPageTop", "btnJOResetPageTop",
  340.                               "sbJOPageBottom", "btnJOResetPageBottom",
  341.                               "cbJOPrettyPrint", "btnJOResetPrettyPrint",
  342.                               "cbJOWrap", "btnJOResetWrap",
  343.                               "sbJOColumns", "btnJOResetColumns",
  344.                               "tblJOOther",
  345.                               "entNewJobOption", "btnNewJobOption",
  346.  
  347.                               # Marker levels
  348.                               "vboxMarkerLevels",
  349.                               "btnRefreshMarkerLevels"]})
  350.  
  351.         # Ensure the default PrintersWindow is shown despite
  352.         # the --no-focus-on-map option
  353.         self.PrintersWindow.set_focus_on_map (self.focus_on_map)
  354.         self.PrintersWindow.show()
  355.  
  356.         # Since some dialogs are reused we can't let the delete-event's
  357.         # default handler destroy them
  358.         for dialog in [self.PrinterPropertiesDialog,
  359.                        self.ServerSettingsDialog]:
  360.             dialog.connect ("delete-event", on_delete_just_hide)
  361.  
  362.         self.ConnectingDialog.connect ("delete-event",
  363.                                        self.on_connectingdialog_delete)
  364.  
  365.         self.tooltips = gtk.Tooltips()
  366.         self.tooltips.enable()
  367.         gtk.window_set_default_icon_name ('printer')
  368.  
  369.         # Toolbar
  370.         # Glade-2 doesn't have support for MenuToolButton, so we do that here.
  371.         self.btnNew = gtk.MenuToolButton ('gtk-new')
  372.         self.btnNew.set_is_important (True)
  373.         newmenu = gtk.Menu ()
  374.         newprinter = gtk.ImageMenuItem (_("Printer"))
  375.         printericon = gtk.Image ()
  376.         printericon.set_from_icon_name ("printer", gtk.ICON_SIZE_MENU)
  377.         newprinter.set_image (printericon)
  378.         newprinter.connect ('activate', self.on_new_printer_activate)
  379.         self.btnNew.connect ('clicked', self.on_new_printer_activate)
  380.         newclass = gtk.ImageMenuItem (_("Class"))
  381.         classicon = gtk.Image ()
  382.         classicon.set_from_icon_name ("gtk-dnd-multiple", gtk.ICON_SIZE_MENU)
  383.         newclass.set_image (classicon)
  384.         newclass.connect ('activate', self.on_new_class_activate)
  385.         newprinter.show ()
  386.         newclass.show ()
  387.         newmenu.attach (newprinter, 0, 1, 0, 1)
  388.         newmenu.attach (newclass, 0, 1, 1, 2)
  389.         self.btnNew.set_menu (newmenu)
  390.         self.toolbar.add (self.btnNew)
  391.         self.toolbar.add (gtk.SeparatorToolItem ())
  392.         refreshbutton = gtk.ToolButton ('gtk-refresh')
  393.         refreshbutton.connect ('clicked', self.on_btnRefresh_clicked)
  394.         self.toolbar.add (refreshbutton)
  395.         self.toolbar.show_all ()
  396.  
  397.         # Printer Actions
  398.         printer_manager_action_group = \
  399.             gtk.ActionGroup ("PrinterManagerActionGroup")
  400.         printer_manager_action_group.add_actions ([
  401.                 ("rename-printer", None, _("_Rename"),
  402.                  None, None, self.on_rename_activate),
  403.                 ("copy-printer", gtk.STOCK_COPY, None,
  404.                  "<Ctrl>c", None, self.on_copy_activate),
  405.                 ("delete-printer", gtk.STOCK_DELETE, None,
  406.                  None, None, self.on_delete_activate),
  407.                 ("set-default-printer", gtk.STOCK_HOME, _("Set As De_fault"),
  408.                  None, None, self.on_set_as_default_activate),
  409.                 ("edit-printer", gtk.STOCK_PROPERTIES, None,
  410.                  None, None, self.on_edit_activate),
  411.                 ("create-class", gtk.STOCK_DND_MULTIPLE, _("_Create class"),
  412.                  None, None, self.on_create_class_activate),
  413.                 ("view-print-queue", gtk.STOCK_FIND, _("View Print _Queue"),
  414.                  None, None, self.on_view_print_queue_activate),
  415.                 ("add-to-group", None, _("_Add to Group"),
  416.                  None, None, None),
  417.                 ("save-as-group", None, _("Save Results as _Group"),
  418.                  None, None, self.on_save_as_group_activate),
  419.                 ("save-as-search-group", None, _("Save Filter as _Search Group"),
  420.                  None, None, self.on_save_as_search_group_activate),
  421.                 ])
  422.         printer_manager_action_group.add_toggle_actions ([
  423.                 ("enable-printer", None, _("E_nabled"),
  424.                  None, None, self.on_enabled_activate),
  425.                 ("share-printer", None, _("_Shared"),
  426.                  None, None, self.on_shared_activate),
  427.                 ])
  428.         printer_manager_action_group.add_radio_actions ([
  429.                 ("filter-name", None, _("Name")),
  430.                 ("filter-description", None, _("Description")),
  431.                 ("filter-location", None, _("Location")),
  432.                 ("filter-manufacturer", None, _("Manufacturer / Model")),
  433.                 ], 1, self.on_filter_criterion_changed)
  434.         for action in printer_manager_action_group.list_actions ():
  435.             action.set_sensitive (False)
  436.         printer_manager_action_group.get_action ("view-print-queue").set_sensitive (True)
  437.         printer_manager_action_group.get_action ("filter-name").set_sensitive (True)
  438.         printer_manager_action_group.get_action ("filter-description").set_sensitive (True)
  439.         printer_manager_action_group.get_action ("filter-location").set_sensitive (True)
  440.         printer_manager_action_group.get_action ("filter-manufacturer").set_sensitive (True)
  441.  
  442.         self.ui_manager = gtk.UIManager ()
  443.         self.ui_manager.insert_action_group (printer_manager_action_group, -1)
  444.         self.ui_manager.add_ui_from_string (
  445. """
  446. <ui>
  447.  <accelerator action="rename-printer"/>
  448.  <accelerator action="copy-printer"/>
  449.  <accelerator action="delete-printer"/>
  450.  <accelerator action="set-default-printer"/>
  451.  <accelerator action="edit-printer"/>
  452.  <accelerator action="create-class"/>
  453.  <accelerator action="view-print-queue"/>
  454.  <accelerator action="add-to-group"/>
  455.  <accelerator action="save-as-group"/>
  456.  <accelerator action="save-as-search-group"/>
  457.  <accelerator action="enable-printer"/>
  458.  <accelerator action="share-printer"/>
  459.  <accelerator action="filter-name"/>
  460.  <accelerator action="filter-description"/>
  461.  <accelerator action="filter-location"/>
  462.  <accelerator action="filter-manufacturer"/>
  463. </ui>
  464. """
  465. )
  466.         self.ui_manager.ensure_update ()
  467.         self.PrintersWindow.add_accel_group (self.ui_manager.get_accel_group ())
  468.  
  469.         self.printer_context_menu = gtk.Menu ()
  470.         for action_name in ["edit-printer",
  471.                             "copy-printer",
  472.                             "rename-printer",
  473.                             "delete-printer",
  474.                             None,
  475.                             "enable-printer",
  476.                             "share-printer",
  477.                             "create-class",
  478.                             "set-default-printer",
  479.                             None,
  480.                             "add-to-group",
  481.                             "view-print-queue"]:
  482.             if not action_name:
  483.                 item = gtk.SeparatorMenuItem ()
  484.             else:
  485.                 action = printer_manager_action_group.get_action (action_name)
  486.                 item = action.create_menu_item ()
  487.             item.show ()
  488.             self.printer_context_menu.append (item)
  489.         self.printer_menubar_item.set_submenu (self.printer_context_menu)
  490.  
  491.         self.jobviewers = [] # to keep track of jobviewer windows
  492.  
  493.         # Printer properties combo boxes
  494.         for combobox in [self.cmbPStartBanner,
  495.                          self.cmbPEndBanner,
  496.                          self.cmbPErrorPolicy,
  497.                          self.cmbPOperationPolicy]:
  498.             cell = gtk.CellRendererText ()
  499.             combobox.clear ()
  500.             combobox.pack_start (cell, True)
  501.             combobox.add_attribute (cell, 'text', 0)
  502.  
  503.         btn = self.btnRefreshMarkerLevels
  504.         btn.connect ("clicked", self.on_btnRefreshMarkerLevels_clicked)
  505.  
  506.         # Printer state reasons list
  507.         column = gtk.TreeViewColumn (_("Message"))
  508.         icon = gtk.CellRendererPixbuf ()
  509.         column.pack_start (icon, False)
  510.         text = gtk.CellRendererText ()
  511.         column.pack_start (text, False)
  512.         column.set_cell_data_func (icon, self.set_printer_state_reason_icon)
  513.         column.set_cell_data_func (text, self.set_printer_state_reason_text)
  514.         column.set_resizable (True)
  515.         self.tvPrinterStateReasons.append_column (column)
  516.         selection = self.tvPrinterStateReasons.get_selection ()
  517.         selection.set_mode (gtk.SELECTION_NONE)
  518.         store = gtk.ListStore (int, str)
  519.         self.tvPrinterStateReasons.set_model (store)
  520.  
  521.         # New Printer Dialog
  522.         self.newPrinterGUI = np = NewPrinterGUI(self)
  523.         np.NewPrinterWindow.set_transient_for(self.PrintersWindow)
  524.  
  525.         # Set up "About" dialog
  526.         self.AboutDialog.set_program_name(domain)
  527.         self.AboutDialog.set_version(config.VERSION)
  528.         self.AboutDialog.set_icon_name('printer')
  529.  
  530.         # Set up "Problems?" link button
  531.         class UnobtrusiveButton(gtk.Button):
  532.             def __init__ (self, **args):
  533.                 gtk.Button.__init__ (self, **args)
  534.                 self.set_relief (gtk.RELIEF_NONE)
  535.                 label = self.get_child ()
  536.                 text = label.get_text ()
  537.                 label.set_use_markup (True)
  538.                 label.set_markup ('<span size="small" ' +
  539.                                   'underline="single" ' +
  540.                                   'color="#0000ee">%s</span>' % text)
  541.  
  542.         problems = UnobtrusiveButton (label=_("Problems?"))
  543.         self.hboxServerBrowse.pack_end (problems, False, False, 0)
  544.         problems.connect ('clicked', self.on_problems_button_clicked)
  545.         problems.show ()
  546.  
  547.         self.static_tabs = 3
  548.  
  549.         gtk_label_autowrap.set_autowrap(self.PrintersWindow)
  550.  
  551.         try:
  552.             self.cups = authconn.Connection(self.PrintersWindow)
  553.         except RuntimeError:
  554.             self.cups = None
  555.  
  556.         self.status_context_id = self.statusbarMain.get_context_id(
  557.             "Connection")
  558.         self.setConnected()
  559.  
  560.         # Setup search and printer groups
  561.         self.setup_toolbar_for_search_entry ()
  562.         self.current_filter_text = ""
  563.         self.current_filter_mode = "filter-name"
  564.  
  565.         self.groups_pane = GroupsPane ()
  566.         self.current_groups_pane_item = self.groups_pane.get_selected_item ()
  567.         self.groups_pane.connect ('item-activated',
  568.                                   self.on_groups_pane_item_activated)
  569.         self.groups_pane.connect ('items-changed',
  570.                                   self.on_groups_pane_items_changed)
  571.         self.PrintersWindow.add_accel_group (
  572.             self.groups_pane.ui_manager.get_accel_group ())
  573.         self.view_area_hpaned = gtk.HPaned ()
  574.         self.view_area_hpaned.add1 (self.groups_pane)
  575.         self.groups_pane_visible = False
  576.         if self.groups_pane.n_groups () > 0:
  577.             self.view_groups.set_active (True)
  578.  
  579.         # Group menubar item
  580.         self.group_menubar_item.set_submenu (self.groups_pane.groups_menu)
  581.  
  582.         # "Add to Group" submenu
  583.         self.add_to_group_menu = gtk.Menu ()
  584.         self.update_add_to_group_menu ()
  585.         action = printer_manager_action_group.get_action ("add-to-group")
  586.         for proxy in action.get_proxies ():
  587.             if isinstance (proxy, gtk.MenuItem):
  588.                 item = proxy
  589.                 break
  590.         item.set_submenu (self.add_to_group_menu)
  591.  
  592.         # Search entry drop down menu
  593.         menu = gtk.Menu ()
  594.         for action_name in ["filter-name",
  595.                             "filter-description",
  596.                             "filter-location",
  597.                             "filter-manufacturer",
  598.                             None,
  599.                             "save-as-group",
  600.                             "save-as-search-group"]:
  601.             if not action_name:
  602.                 item = gtk.SeparatorMenuItem ()
  603.             else:
  604.                 action = printer_manager_action_group.get_action (action_name)
  605.                 item = action.create_menu_item ()
  606.             menu.append (item)
  607.         menu.show_all ()
  608.         self.search_entry.set_drop_down_menu (menu)
  609.  
  610.         # Setup icon view
  611.         self.mainlist = gtk.ListStore(gobject.TYPE_PYOBJECT, # Object
  612.                                       gtk.gdk.Pixbuf,        # Pixbuf
  613.                                       gobject.TYPE_STRING,   # Name
  614.                                       gobject.TYPE_STRING)   # Tooltip
  615.  
  616.         self.dests_iconview.set_model(self.mainlist)
  617.         self.dests_iconview.set_column_spacing (30)
  618.         self.dests_iconview.set_row_spacing (20)
  619.         self.dests_iconview.set_pixbuf_column (1)
  620.         self.dests_iconview.set_text_column (2)
  621.         self.dests_iconview.set_tooltip_column (3)
  622.         self.dests_iconview.connect ('key-press-event',
  623.                                      self.dests_iconview_key_press_event)
  624.         self.dests_iconview.connect ('item-activated',
  625.                                      self.dests_iconview_item_activated)
  626.         self.dests_iconview.connect ('selection-changed',
  627.                                      self.dests_iconview_selection_changed)
  628.         self.dests_iconview.connect ('button-press-event',
  629.                                      self.dests_iconview_button_press_event)
  630.         self.dests_iconview.connect ('popup-menu',
  631.                                      self.dests_iconview_popup_menu)
  632.         self.dests_iconview_selection_changed (self.dests_iconview)
  633.         self.dests_iconview.enable_model_drag_source (gtk.gdk.BUTTON1_MASK,
  634.                                                       # should use a variable
  635.                                                       # instead of 0
  636.                                                       [("queue", 0, 0)],
  637.                                                       gtk.gdk.ACTION_COPY)
  638.         self.dests_iconview.connect ("drag-data-get",
  639.                                      self.dests_iconview_drag_data_get)
  640.  
  641.         # setup some lists
  642.         m = gtk.SELECTION_MULTIPLE
  643.         s = gtk.SELECTION_SINGLE
  644.         for name, treeview, selection_mode in (
  645.             (_("Members of this class"), self.tvClassMembers, m),
  646.             (_("Others"), self.tvClassNotMembers, m),
  647.             (_("Members of this class"), np.tvNCMembers, m),
  648.             (_("Others"), np.tvNCNotMembers, m),
  649.             (_("Devices"), np.tvNPDevices, s),
  650.             (_("Connections"), np.tvNPDeviceURIs, s),
  651.             (_("Makes"), np.tvNPMakes,s),
  652.             (_("Models"), np.tvNPModels,s),
  653.             (_("Drivers"), np.tvNPDrivers,s),
  654.             (_("Downloadable Drivers"), np.tvNPDownloadableDrivers,s),
  655.             (_("Users"), self.tvPUsers, m),
  656.             ):
  657.  
  658.             model = gtk.ListStore(str)
  659.             cell = gtk.CellRendererText()
  660.             column = gtk.TreeViewColumn(name, cell, text=0)
  661.             treeview.set_model(model)
  662.             treeview.append_column(column)
  663.             treeview.get_selection().set_mode(selection_mode)
  664.  
  665.         # Server Settings dialog
  666.         self.ServerSettingsDialog.connect ('response',
  667.                                            self.server_settings_response)
  668.  
  669.         # Printer Properties dialog
  670.         self.PrinterPropertiesDialog.connect ('response',
  671.                                               self.printer_properties_response)
  672.  
  673.         # Printer Properties tree view
  674.         col = gtk.TreeViewColumn ('', gtk.CellRendererText (), markup=0)
  675.         self.tvPrinterProperties.append_column (col)
  676.         sel = self.tvPrinterProperties.get_selection ()
  677.         sel.connect ('changed', self.on_tvPrinterProperties_selection_changed)
  678.         sel.set_mode (gtk.SELECTION_SINGLE)
  679.  
  680.         # Job Options widgets.
  681.         opts = [ options.OptionAlwaysShown ("copies", int, 1,
  682.                                             self.sbJOCopies,
  683.                                             self.btnJOResetCopies),
  684.  
  685.                  options.OptionAlwaysShownSpecial \
  686.                  ("orientation-requested", int, 3,
  687.                   self.cmbJOOrientationRequested,
  688.                   self.btnJOResetOrientationRequested,
  689.                   combobox_map = [3, 4, 5, 6],
  690.                   special_choice=_("Automatic rotation")),
  691.  
  692.                  options.OptionAlwaysShown ("fitplot", bool, False,
  693.                                             self.cbJOFitplot,
  694.                                             self.btnJOResetFitplot),
  695.  
  696.                  options.OptionAlwaysShown ("number-up", int, 1,
  697.                                             self.cmbJONumberUp,
  698.                                             self.btnJOResetNumberUp,
  699.                                             combobox_map=[1, 2, 4, 6, 9, 16]),
  700.  
  701.                  options.OptionAlwaysShown ("number-up-layout", str, "lrtb",
  702.                                             self.cmbJONumberUpLayout,
  703.                                             self.btnJOResetNumberUpLayout,
  704.                                             combobox_map = [ "lrtb",
  705.                                                              "lrbt",
  706.                                                              "rltb",
  707.                                                              "rlbt",
  708.                                                              "tblr",
  709.                                                              "tbrl",
  710.                                                              "btlr",
  711.                                                              "btrl" ]),
  712.  
  713.                  options.OptionAlwaysShown ("brightness", int, 100,
  714.                                             self.sbJOBrightness,
  715.                                             self.btnJOResetBrightness),
  716.  
  717.                  options.OptionAlwaysShown ("finishings", int, 3,
  718.                                             self.cmbJOFinishings,
  719.                                             self.btnJOResetFinishings,
  720.                                             combobox_map = [ 3, 4, 5, 6,
  721.                                                              7, 8, 9, 10,
  722.                                                              11, 12, 13, 14,
  723.                                                              20, 21, 22, 23,
  724.                                                              24, 25, 26, 27,
  725.                                                              28, 29, 30, 31,
  726.                                                              50, 51, 52, 53 ],
  727.                                             use_supported = True),
  728.  
  729.                  options.OptionAlwaysShown ("job-priority", int, 50,
  730.                                             self.sbJOJobPriority,
  731.                                             self.btnJOResetJobPriority),
  732.  
  733.                  options.OptionAlwaysShown ("media", str,
  734.                                             "A4", # This is the default for
  735.                                                   # when media-default is
  736.                                                   # not supplied by the IPP
  737.                                                   # server.  Fortunately it
  738.                                                   # is a mandatory attribute.
  739.                                             self.cmbJOMedia,
  740.                                             self.btnJOResetMedia,
  741.                                             use_supported = True),
  742.  
  743.                  options.OptionAlwaysShown ("sides", str, "one-sided",
  744.                                             self.cmbJOSides,
  745.                                             self.btnJOResetSides,
  746.                                             combobox_map =
  747.                                             [ "one-sided",
  748.                                               "two-sided-long-edge",
  749.                                               "two-sided-short-edge" ]),
  750.  
  751.                  options.OptionAlwaysShown ("job-hold-until", str,
  752.                                             "no-hold",
  753.                                             self.cmbJOHoldUntil,
  754.                                             self.btnJOResetHoldUntil,
  755.                                             use_supported = True),
  756.  
  757.                  options.OptionAlwaysShown ("mirror", bool, False,
  758.                                             self.cbJOMirror,
  759.                                             self.btnJOResetMirror),
  760.  
  761.                  options.OptionAlwaysShown ("scaling", int, 100,
  762.                                             self.sbJOScaling,
  763.                                             self.btnJOResetScaling),
  764.  
  765.                  options.OptionAlwaysShown ("saturation", int, 100,
  766.                                             self.sbJOSaturation,
  767.                                             self.btnJOResetSaturation),
  768.  
  769.                  options.OptionAlwaysShown ("hue", int, 0,
  770.                                             self.sbJOHue,
  771.                                             self.btnJOResetHue),
  772.  
  773.                  options.OptionAlwaysShown ("gamma", int, 1000,
  774.                                             self.sbJOGamma,
  775.                                             self.btnJOResetGamma),
  776.  
  777.                  options.OptionAlwaysShown ("cpi", float, 10.0,
  778.                                             self.sbJOCpi, self.btnJOResetCpi),
  779.  
  780.                  options.OptionAlwaysShown ("lpi", float, 6.0,
  781.                                             self.sbJOLpi, self.btnJOResetLpi),
  782.  
  783.                  options.OptionAlwaysShown ("page-left", int, 0,
  784.                                             self.sbJOPageLeft,
  785.                                             self.btnJOResetPageLeft),
  786.  
  787.                  options.OptionAlwaysShown ("page-right", int, 0,
  788.                                             self.sbJOPageRight,
  789.                                             self.btnJOResetPageRight),
  790.  
  791.                  options.OptionAlwaysShown ("page-top", int, 0,
  792.                                             self.sbJOPageTop,
  793.                                             self.btnJOResetPageTop),
  794.  
  795.                  options.OptionAlwaysShown ("page-bottom", int, 0,
  796.                                             self.sbJOPageBottom,
  797.                                             self.btnJOResetPageBottom),
  798.  
  799.                  options.OptionAlwaysShown ("prettyprint", bool, False,
  800.                                             self.cbJOPrettyPrint,
  801.                                             self.btnJOResetPrettyPrint),
  802.  
  803.                  options.OptionAlwaysShown ("wrap", bool, False, self.cbJOWrap,
  804.                                             self.btnJOResetWrap),
  805.  
  806.                  options.OptionAlwaysShown ("columns", int, 1,
  807.                                             self.sbJOColumns,
  808.                                             self.btnJOResetColumns),
  809.                  ]
  810.         self.job_options_widgets = {}
  811.         self.job_options_buttons = {}
  812.         for option in opts:
  813.             self.job_options_widgets[option.widget] = option
  814.             self.job_options_buttons[option.button] = option
  815.  
  816.         self.monitor = monitor.Monitor (self, monitor_jobs=False)
  817.  
  818.         try:
  819.             self.populateList()
  820.         except cups.HTTPError, (s,):
  821.             self.cups = None
  822.             self.setConnected()
  823.             self.populateList()
  824.             show_HTTP_Error(s, self.PrintersWindow)
  825.  
  826.         # Attempt to size the window appropriately.
  827.         try:
  828.             (max_width, max_height) = (600, 400)
  829.             self.dests_iconview.resize_children ()
  830.             (width, height) = self.dests_iconview.size_request ()
  831.             if height > max_height:
  832.                 # Too tall.  Try at maximum width.
  833.                 self.dests_iconview.set_size_request (max_width, -1)
  834.                 self.dests_iconview.resize_children ()
  835.                 while gtk.events_pending ():
  836.                     gtk.main_iteration ()
  837.                 (width, height) = self.dests_iconview.size_request ()
  838.  
  839.                 # Finally, limit both the width and height.
  840.                 (width, height) = map (min,
  841.                                        (width, height),
  842.                                        (max_width, max_height))
  843.  
  844.             self.dests_iconview.set_size_request (width, height)
  845.             while gtk.events_pending ():
  846.                 gtk.main_iteration ()
  847.             (width, height) = self.PrintersWindow.get_size ()
  848.             self.dests_iconview.set_size_request (-1, -1)
  849.             self.PrintersWindow.resize (width, height)
  850.         except:
  851.             nonfatalException ()
  852.  
  853.         if setup_printer:
  854.             self.device_uri = setup_printer
  855.             self.devid = devid
  856.             self.ppd = None
  857.             try:
  858.                 self.on_autodetected_printer_without_driver(None)
  859.             except RuntimeError:
  860.                 pass
  861.  
  862.         if configure_printer:
  863.             # Need to find the entry in the iconview model and activate it.
  864.             try:
  865.                 self.display_properties_dialog_for (configure_printer)
  866.                 if print_test_page:
  867.                     self.btnPrintTestPage.clicked ()
  868.                 if change_ppd:
  869.                     self.btnChangePPD.clicked ()
  870.             except RuntimeError:
  871.                 pass
  872.  
  873.     def display_properties_dialog_for (self, queue):
  874.         model = self.dests_iconview.get_model ()
  875.         iter = model.get_iter_first ()
  876.         while iter != None:
  877.             name = unicode (model.get_value (iter, 2))
  878.             if name == queue:
  879.                 path = model.get_path (iter)
  880.                 self.dests_iconview.scroll_to_path (path, True, 0.5, 0.5)
  881.                 self.dests_iconview.set_cursor (path)
  882.                 self.dests_iconview.item_activated (path)
  883.                 break
  884.             iter = model.iter_next (iter)
  885.  
  886.         if iter == None:
  887.             raise RuntimeError
  888.  
  889.     def setup_toolbar_for_search_entry (self):
  890.         separator = gtk.SeparatorToolItem ()
  891.         separator.set_draw (False)
  892.  
  893.         self.toolbar.insert (separator, -1)
  894.         self.toolbar.child_set_property (separator, "expand", True)
  895.  
  896.         self.search_entry = ToolbarSearchEntry ()
  897.         self.search_entry.connect ('search', self.on_search_entry_search)
  898.  
  899.         tool_item = gtk.ToolItem ()
  900.         tool_item.add (self.search_entry)
  901.         self.toolbar.insert (tool_item, -1)
  902.         self.toolbar.show_all ()
  903.  
  904.     def on_search_entry_search (self, UNUSED, text):
  905.         self.ui_manager.get_action ("/save-as-group").set_sensitive (
  906.             text and True or False)
  907.         self.ui_manager.get_action ("/save-as-search-group").set_sensitive (
  908.             text and True or False)
  909.         self.current_filter_text = text
  910.         self.populateList ()
  911.  
  912.     def on_groups_pane_item_activated (self, UNUSED, item):
  913.         self.search_entry.clear ()
  914.  
  915.         if isinstance (item, SavedSearchGroupItem):
  916.             crit = item.criteria[0]
  917.             if crit.subject == SearchCriterion.SUBJECT_NAME:
  918.                 self.ui_manager.get_action ("/filter-name").activate ()
  919.             elif crit.subject == SearchCriterion.SUBJECT_DESC:
  920.                 self.ui_manager.get_action ("/filter-description").activate ()
  921.             elif crit.subject == SearchCriterion.SUBJECT_LOCATION:
  922.                 self.ui_manager.get_action ("/filter-location").activate ()
  923.             elif crit.subject == SearchCriterion.SUBJECT_MANUF:
  924.                 self.ui_manager.get_action ("/filter-manufacturer").activate ()
  925.             else:
  926.                 nonfatalException ()
  927.  
  928.             self.search_entry.set_text (crit.value)
  929.  
  930.         self.current_groups_pane_item = item
  931.         self.populateList ()
  932.  
  933.     def on_add_to_group_menu_item_activate (self, menuitem, group):
  934.         group.add_queues (self.groups_pane.currently_selected_queues)
  935.  
  936.     def update_add_to_group_menu (self):
  937.         for child in self.add_to_group_menu.get_children ():
  938.             self.add_to_group_menu.remove (child)
  939.         static_groups = self.groups_pane.get_static_groups ()
  940.         for group in static_groups:
  941.             item = gtk.MenuItem (group.name, False)
  942.             item.connect ("activate",
  943.                           self.on_add_to_group_menu_item_activate, group)
  944.             self.add_to_group_menu.append (item)
  945.         if len (static_groups) > 0:
  946.             item = gtk.SeparatorMenuItem ()
  947.             self.add_to_group_menu.append (item)
  948.         action = self.groups_pane.ui_manager.get_action ("/new-group-from-selection")
  949.         item = action.create_menu_item ()
  950.         self.add_to_group_menu.append (item)
  951.         self.add_to_group_menu.show_all ()
  952.  
  953.     def on_groups_pane_items_changed (self, UNUSED):
  954.         if not self.groups_pane_visible:
  955.             self.view_groups.set_active (True)
  956.         self.update_add_to_group_menu ()
  957.  
  958.     def on_filter_criterion_changed (self, UNUSED, selected_action):
  959.         self.current_filter_mode = selected_action.get_name ()
  960.         self.populateList ()
  961.  
  962.     def dests_iconview_item_activated (self, iconview, path):
  963.         model = iconview.get_model ()
  964.         iter = model.get_iter (path)
  965.         name = unicode (model.get_value (iter, 2))
  966.         object = model.get_value (iter, 0)
  967.  
  968.         try:
  969.             self.fillPrinterTab (name)
  970.         except cups.IPPError, (e, m):
  971.             show_IPP_Error (e, m, self.PrintersWindow)
  972.             if e == cups.IPP_SERVICE_UNAVAILABLE:
  973.                 self.cups = None
  974.                 self.setConnected ()
  975.                 self.populateList ()
  976.             return
  977.         except RuntimeError:
  978.             # Perhaps cupsGetPPD2 failed for a browsed printer.
  979.             return
  980.  
  981.         self.PrinterPropertiesDialog.set_transient_for (self.PrintersWindow)
  982.         for button in [self.btnPrinterPropertiesCancel,
  983.                        self.btnPrinterPropertiesOK,
  984.                        self.btnPrinterPropertiesApply]:
  985.             if object.discovered:
  986.                 button.hide ()
  987.             else:
  988.                 button.show ()
  989.         if object.discovered:
  990.             self.btnPrinterPropertiesClose.show ()
  991.         else:
  992.             self.btnPrinterPropertiesClose.hide ()
  993.         self.setDataButtonState ()
  994.         treeview = self.tvPrinterProperties
  995.         treeview.set_cursor ((0,))
  996.         host = CUPS_server_hostname ()
  997.         self.PrinterPropertiesDialog.set_title (_("Printer Properties - "
  998.                                                   "'%s' on %s") % (name, host))
  999.         self.PrinterPropertiesDialog.set_focus_on_map (self.focus_on_map)
  1000.         self.PrinterPropertiesDialog.show ()
  1001.  
  1002.     def printer_properties_response (self, dialog, response):
  1003.         if response == gtk.RESPONSE_REJECT:
  1004.             # The Conflict button was pressed.
  1005.             message = _("There are conflicting options.\n"
  1006.                         "Changes can only be applied after\n"
  1007.                         "these conflicts are resolved.")
  1008.             message += "\n\n"
  1009.             for option in self.conflicts:
  1010.                 message += option.option.text + "\n"
  1011.  
  1012.             dialog = gtk.MessageDialog(self.PrinterPropertiesDialog,
  1013.                                        gtk.DIALOG_DESTROY_WITH_PARENT |
  1014.                                        gtk.DIALOG_MODAL,
  1015.                                        gtk.MESSAGE_WARNING,
  1016.                                        gtk.BUTTONS_CLOSE,
  1017.                                        message)
  1018.             dialog.run()
  1019.             dialog.destroy()
  1020.             return
  1021.  
  1022.         if (response == gtk.RESPONSE_OK or
  1023.             response == gtk.RESPONSE_APPLY):
  1024.             success = self.save_printer (self.printer)
  1025.  
  1026.         if response == gtk.RESPONSE_APPLY:
  1027.             try:
  1028.                 self.fillPrinterTab (self.printer.name)
  1029.             except:
  1030.                 pass
  1031.  
  1032.             self.setDataButtonState ()
  1033.  
  1034.         if ((response == gtk.RESPONSE_OK and not success) or
  1035.             response == gtk.RESPONSE_CANCEL):
  1036.             dialog.hide ()
  1037.  
  1038.     def dests_iconview_selection_changed (self, iconview):
  1039.         self.updating_widgets = True
  1040.         paths = iconview.get_selected_items ()
  1041.         any_disabled = False
  1042.         any_enabled = False
  1043.         any_discovered = False
  1044.         any_shared = False
  1045.         any_unshared = False
  1046.         self.groups_pane.currently_selected_queues = []
  1047.         model = iconview.get_model ()
  1048.         for path in paths:
  1049.             iter = model.get_iter (path)
  1050.             object = model.get_value (iter, 0)
  1051.             name = unicode (model.get_value (iter, 2))
  1052.             self.groups_pane.currently_selected_queues.append (name)
  1053.             if object.discovered:
  1054.                 any_discovered = True
  1055.             if object.enabled:
  1056.                 any_enabled = True
  1057.             else:
  1058.                 any_disabled = True
  1059.             if object.is_shared:
  1060.                 any_shared = True
  1061.             else:
  1062.                 any_unshared = True
  1063.  
  1064.         n = len (paths)
  1065.         self.groups_pane.ui_manager.get_action (
  1066.             "/new-group-from-selection").set_sensitive (n > 0)
  1067.  
  1068.         self.ui_manager.get_action ("/edit-printer").set_sensitive (n == 1)
  1069.  
  1070.         self.ui_manager.get_action ("/copy-printer").set_sensitive (n == 1)
  1071.  
  1072.         self.ui_manager.get_action ("/rename-printer").set_sensitive (
  1073.             n == 1 and not any_discovered)
  1074.  
  1075.         userdef = userdefault.UserDefaultPrinter ().get ()
  1076.         if (n != 1 or
  1077.             (userdef == None and self.default_printer == name)):
  1078.             set_default_sensitivity = False
  1079.         else:
  1080.             set_default_sensitivity = True
  1081.  
  1082.         self.ui_manager.get_action ("/set-default-printer").set_sensitive (
  1083.             set_default_sensitivity)
  1084.  
  1085.         action = self.ui_manager.get_action ("/enable-printer")
  1086.         action.set_sensitive (n > 0 and not any_discovered)
  1087.         for widget in action.get_proxies ():
  1088.             if isinstance (widget, gtk.CheckMenuItem):
  1089.                 widget.set_inconsistent (n > 1 and any_enabled and any_disabled)
  1090.         action.set_active (any_discovered or not any_disabled)
  1091.  
  1092.         action = self.ui_manager.get_action ("/share-printer")
  1093.         action.set_sensitive (n > 0 and not any_discovered)
  1094.         for widget in action.get_proxies ():
  1095.             if isinstance (widget, gtk.CheckMenuItem):
  1096.                 widget.set_inconsistent (n > 1 and any_shared and any_unshared)
  1097.         action.set_active (any_discovered or not any_unshared)
  1098.  
  1099.         self.ui_manager.get_action ("/delete-printer").set_sensitive (
  1100.             n > 0 and not any_discovered)
  1101.  
  1102.         self.ui_manager.get_action ("/create-class").set_sensitive (n > 1)
  1103.  
  1104.         self.ui_manager.get_action ("/add-to-group").set_sensitive (n > 0)
  1105.  
  1106.         self.updating_widgets = False
  1107.  
  1108.     def dests_iconview_popup_menu (self, iconview):
  1109.         self.printer_context_menu.popup (None, None, None, 0, 0L)
  1110.  
  1111.     def dests_iconview_button_press_event (self, iconview, event):
  1112.         if event.button > 1:
  1113.             click_path = iconview.get_path_at_pos (int (event.x),
  1114.                                                    int (event.y))
  1115.             paths = iconview.get_selected_items ()
  1116.             if click_path == None:
  1117.                 iconview.unselect_all ()
  1118.             elif click_path not in paths:
  1119.                 iconview.unselect_all ()
  1120.                 iconview.select_path (click_path)
  1121.                 cells = iconview.get_cells ()
  1122.                 for cell in cells:
  1123.                     if type (cell) == gtk.CellRendererText:
  1124.                         break
  1125.                 iconview.set_cursor (click_path, cell)
  1126.             self.printer_context_menu.popup (None, None, None,
  1127.                                              event.button, event.time)
  1128.         return False
  1129.  
  1130.     def dests_iconview_key_press_event (self, iconview, event):
  1131.         modifiers = gtk.accelerator_get_default_mod_mask ()
  1132.  
  1133.         if ((event.keyval == gtk.keysyms.BackSpace or
  1134.              event.keyval == gtk.keysyms.Delete or
  1135.              event.keyval == gtk.keysyms.KP_Delete) and
  1136.             ((event.state & modifiers) == 0)):
  1137.  
  1138.             self.ui_manager.get_action ("/delete-printer").activate ()
  1139.             return True
  1140.  
  1141.         if ((event.keyval == gtk.keysyms.F2) and
  1142.             ((event.state & modifiers) == 0)):
  1143.  
  1144.             self.ui_manager.get_action ("/rename-printer").activate ()
  1145.             return True
  1146.  
  1147.         return False
  1148.  
  1149.     def dests_iconview_drag_data_get (self, iconview, context,
  1150.                                       selection_data, info, timestamp):
  1151.         if info == 0: # FIXME: should use an "enum" here
  1152.             model = iconview.get_model ()
  1153.             paths = iconview.get_selected_items ()
  1154.             selected_printer_names = ""
  1155.             for path in paths:
  1156.                 selected_printer_names += \
  1157.                     model.get_value (model.get_iter (path), 2) + "\n"
  1158.  
  1159.             if len (selected_printer_names) > 0:
  1160.                 selection_data.set ("queue", 8, selected_printer_names)
  1161.         else:
  1162.             nonfatalException ()
  1163.  
  1164.     def on_server_settings_activate (self, menuitem):
  1165.         try:
  1166.             self.fillServerTab ()
  1167.         except cups.IPPError:
  1168.             # Not authorized.
  1169.             return
  1170.  
  1171.         self.ServerSettingsDialog.set_transient_for (self.PrintersWindow)
  1172.         self.ServerSettingsDialog.show ()
  1173.  
  1174.     def server_settings_response (self, dialog, response):
  1175.         if response == gtk.RESPONSE_OK:
  1176.             # OK
  1177.             if not self.save_serversettings ():
  1178.                 dialog.hide ()
  1179.         elif response == gtk.RESPONSE_YES:
  1180.             # Advanced
  1181.             try:
  1182.                 AdvancedServerSettingsDialog (self.cups, dialog,
  1183.                                               self.on_adv_server_settings_apply)
  1184.             except:
  1185.                 return
  1186.         else:
  1187.             dialog.hide ()
  1188.  
  1189.     def on_adv_server_settings_apply (self):
  1190.         try:
  1191.             self.fillServerTab ()
  1192.         except cups.IPPError:
  1193.             dialog.hide ()
  1194.  
  1195.     def busy (self, win = None):
  1196.         try:
  1197.             if not win:
  1198.                 win = self.PrintersWindow
  1199.             gdkwin = win.window
  1200.             if gdkwin:
  1201.                 gdkwin.set_cursor (busy_cursor)
  1202.                 while gtk.events_pending ():
  1203.                     gtk.main_iteration ()
  1204.         except:
  1205.             nonfatalException ()
  1206.  
  1207.     def ready (self, win = None):
  1208.         try:
  1209.             if not win:
  1210.                 win = self.PrintersWindow
  1211.             gdkwin = win.window
  1212.             if gdkwin:
  1213.                 gdkwin.set_cursor (ready_cursor)
  1214.                 while gtk.events_pending ():
  1215.                     gtk.main_iteration ()
  1216.         except:
  1217.             nonfatalException ()
  1218.  
  1219.     def setConnected(self):
  1220.         connected = bool(self.cups)
  1221.  
  1222.         host = CUPS_server_hostname ()
  1223.         self.PrintersWindow.set_title(_("Printer configuration - %s") % host)
  1224.         self.PrintersWindow.set_focus_on_map (self.focus_on_map)
  1225.  
  1226.         if connected:
  1227.             status_msg = _("Connected to %s") % host
  1228.         else:
  1229.             status_msg = _("Not connected")
  1230.         self.statusbarMain.push(self.status_context_id, status_msg)
  1231.  
  1232.         for widget in (self.btnNew,
  1233.                        self.new_printer, self.new_class,
  1234.                        self.chkServerBrowse, self.chkServerShare,
  1235.                        self.chkServerRemoteAdmin,
  1236.                        self.chkServerAllowCancelAll,
  1237.                        self.chkServerLogDebug,
  1238.                        self.server_settings_menu_entry):
  1239.             widget.set_sensitive(connected)
  1240.  
  1241.         sharing = self.chkServerShare.get_active ()
  1242.         self.chkServerShareAny.set_sensitive (sharing)
  1243.  
  1244.         try:
  1245.             del self.server_settings
  1246.         except:
  1247.             pass
  1248.  
  1249.     def getServers(self):
  1250.         self.servers.discard(None)
  1251.         known_servers = list(self.servers)
  1252.         known_servers.sort()
  1253.         return known_servers
  1254.  
  1255.     def populateList(self, prompt_allowed=True):
  1256.         # Save selection of printers.
  1257.         selected_printers = set()
  1258.         paths = self.dests_iconview.get_selected_items ()
  1259.         model = self.dests_iconview.get_model ()
  1260.         for path in paths:
  1261.             iter = model.get_iter (path)
  1262.             name = unicode (model.get_value (iter, 2))
  1263.             selected_printers.add (name)
  1264.  
  1265.         if self.cups:
  1266.             self.cups._set_prompt_allowed (prompt_allowed)
  1267.             self.cups._begin_operation (_("obtaining queue details"))
  1268.             try:
  1269.                 # get Printers
  1270.                 self.printers = cupshelpers.getPrinters(self.cups)
  1271.  
  1272.                 # Get default printer.
  1273.                 self.default_printer = self.cups.getDefault ()
  1274.             except cups.IPPError, (e, m):
  1275.                 show_IPP_Error(e, m, self.PrintersWindow)
  1276.                 self.printers = {}
  1277.                 self.default_printer = None
  1278.  
  1279.             self.cups._end_operation ()
  1280.             self.cups._set_prompt_allowed (True)
  1281.         else:
  1282.             self.printers = {}
  1283.             self.default_printer = None
  1284.  
  1285.         for name, printer in self.printers.iteritems():
  1286.             self.servers.add(printer.getServer())
  1287.  
  1288.         userdef = userdefault.UserDefaultPrinter ().get ()
  1289.  
  1290.         local_printers = []
  1291.         local_classes = []
  1292.         remote_printers = []
  1293.         remote_classes = []
  1294.  
  1295.         # Choose a view according to the groups pane item
  1296.         if (isinstance (self.current_groups_pane_item, AllPrintersItem) or
  1297.             isinstance (self.current_groups_pane_item, SavedSearchGroupItem)):
  1298.             delete_action = self.ui_manager.get_action ("/delete-printer")
  1299.             delete_action.set_properties (label = None)
  1300.             printers_set = self.printers
  1301.         elif isinstance (self.current_groups_pane_item, FavouritesItem):
  1302.             printers_set = {} # FIXME
  1303.         elif isinstance (self.current_groups_pane_item, StaticGroupItem):
  1304.             delete_action = self.ui_manager.get_action ("/delete-printer")
  1305.             delete_action.set_properties (label = _("Remove from Group"))
  1306.             printers_set = {}
  1307.             deleted_printers = []
  1308.             for printer_name in self.current_groups_pane_item.printer_queues:
  1309.                 try:
  1310.                     printer = self.printers[printer_name]
  1311.                     printers_set[printer_name] = printer
  1312.                 except KeyError:
  1313.                     deleted_printers.append (printer_name)
  1314.             self.current_groups_pane_item.remove_queues (deleted_printers)
  1315.         else:
  1316.             printers_set = self.printers
  1317.             nonfatalException ()
  1318.  
  1319.         # Filter printers
  1320.         if len (self.current_filter_text) > 0:
  1321.             printers_subset = {}
  1322.             pattern = re.compile (self.current_filter_text, re.I) # ignore case
  1323.  
  1324.             if self.current_filter_mode == "filter-name":
  1325.                 for name in printers_set.keys ():
  1326.                     if pattern.search (name) != None:
  1327.                         printers_subset[name] = printers_set[name]
  1328.             elif self.current_filter_mode == "filter-description":
  1329.                 for name, printer in printers_set.iteritems ():
  1330.                     if pattern.search (printer.info) != None:
  1331.                         printers_subset[name] = printers_set[name]
  1332.             elif self.current_filter_mode == "filter-location":
  1333.                 for name, printer in printers_set.iteritems ():
  1334.                     if pattern.search (printer.location) != None:
  1335.                         printers_subset[name] = printers_set[name]
  1336.             elif self.current_filter_mode == "filter-manufacturer":
  1337.                 for name, printer in printers_set.iteritems ():
  1338.                     if pattern.search (printer.make_and_model) != None:
  1339.                         printers_subset[name] = printers_set[name]
  1340.             else:
  1341.                 nonfatalException ()
  1342.  
  1343.             printers_set = printers_subset
  1344.  
  1345.         if not self.view_discovered_printers.get_active ():
  1346.             printers_subset = {}
  1347.             for name, printer in printers_set.iteritems ():
  1348.                 if not printer.discovered:
  1349.                     printers_subset[name] = printer
  1350.  
  1351.             printers_set = printers_subset
  1352.  
  1353.         for name, printer in printers_set.iteritems():
  1354.             if printer.remote:
  1355.                 if printer.is_class: remote_classes.append(name)
  1356.                 else: remote_printers.append(name)
  1357.             else:
  1358.                 if printer.is_class: local_classes.append(name)
  1359.                 else: local_printers.append(name)
  1360.  
  1361.         local_printers.sort()
  1362.         local_classes.sort()
  1363.         remote_printers.sort()
  1364.         remote_classes.sort()
  1365.  
  1366.         # remove old printers/classes
  1367.         self.mainlist.clear ()
  1368.  
  1369.         # add new
  1370.         PRINTER_TYPE = { 'discovered-printer':
  1371.                              (_("Network printer (discovered)"),
  1372.                               'i-network-printer'),
  1373.                          'discovered-class':
  1374.                              (_("Network class (discovered)"),
  1375.                               'i-network-printer'),
  1376.                          'local-printer':
  1377.                              (_("Printer"),
  1378.                               'gnome-dev-printer'),
  1379.                          'local-fax':
  1380.                              (_("Fax"),
  1381.                               'gnome-dev-printer'),
  1382.                          'local-class':
  1383.                              (_("Class"),
  1384.                               'gnome-dev-printer'),
  1385.                          'ipp-printer':
  1386.                              (_("Network printer"),
  1387.                               'i-network-printer'),
  1388.                          'smb-printer':
  1389.                              (_("Network print share"),
  1390.                               'gnome-dev-printer'),
  1391.                          'network-printer':
  1392.                              (_("Network printer"),
  1393.                               'i-network-printer'),
  1394.                          }
  1395.         theme = gtk.icon_theme_get_default ()
  1396.         for printers in (local_printers,
  1397.                          local_classes,
  1398.                          remote_printers,
  1399.                          remote_classes):
  1400.             if not printers: continue
  1401.             for name in printers:
  1402.                 type = 'local-printer'
  1403.                 object = printers_set[name]
  1404.                 if object.discovered:
  1405.                     if object.is_class:
  1406.                         type = 'discovered-class'
  1407.                     else:
  1408.                         type = 'discovered-printer'
  1409.                 elif object.is_class:
  1410.                     type = 'local-class'
  1411.                 else:
  1412.                     (scheme, rest) = urllib.splittype (object.device_uri)
  1413.                     if scheme == 'ipp':
  1414.                         type = 'ipp-printer'
  1415.                     elif scheme == 'smb':
  1416.                         type = 'smb-printer'
  1417.                     elif scheme == 'hpfax':
  1418.                         type = 'local-fax'
  1419.                     elif scheme in ['socket', 'lpd']:
  1420.                         type = 'network-printer'
  1421.  
  1422.                 (tip, icon) = PRINTER_TYPE[type]
  1423.                 (w, h) = gtk.icon_size_lookup (gtk.ICON_SIZE_DIALOG)
  1424.                 try:
  1425.                     pixbuf = theme.load_icon (icon, w, 0)
  1426.                 except gobject.GError:
  1427.                     # Not in theme.
  1428.                     pixbuf = None
  1429.                     for p in [iconpath, 'icons/']:
  1430.                         try:
  1431.                             pixbuf = gtk.gdk.pixbuf_new_from_file ("%s%s.png" %
  1432.                                                                    (p, icon))
  1433.                             break
  1434.                         except gobject.GError:
  1435.                             pass
  1436.  
  1437.                     if pixbuf == None:
  1438.                         try:
  1439.                             pixbuf = theme.load_icon ('printer', w, 0)
  1440.                         except:
  1441.                             # Just create an empty pixbuf.
  1442.                             pixbuf = gtk.gdk.Pixbuf (gtk.gdk.COLORSPACE_RGB,
  1443.                                                      True, 8, w, h)
  1444.                             pixbuf.fill (0)
  1445.  
  1446.                 emblem = None
  1447.                 if name == self.default_printer:
  1448.                     emblem = 'emblem-default'
  1449.                 elif name == userdef:
  1450.                     emblem = 'emblem-favorite'
  1451.  
  1452.                 if emblem:
  1453.                     (w, h) = gtk.icon_size_lookup (gtk.ICON_SIZE_DIALOG)
  1454.                     default_emblem = theme.load_icon (emblem, w/2, 0)
  1455.                     copy = pixbuf.copy ()
  1456.                     default_emblem.composite (copy, 0, 0,
  1457.                                               copy.get_width (),
  1458.                                               copy.get_height (),
  1459.                                               0, 0,
  1460.                                               1.0, 1.0,
  1461.                                               gtk.gdk.INTERP_NEAREST, 255)
  1462.                     pixbuf = copy
  1463.  
  1464.                 self.mainlist.append (row=[object, pixbuf, name, tip])
  1465.  
  1466.         # Restore selection of printers.
  1467.         model = self.dests_iconview.get_model ()
  1468.         def maybe_select (model, path, iter):
  1469.             name = unicode (model.get_value (iter, 2))
  1470.             if name in selected_printers:
  1471.                 self.dests_iconview.select_path (path)
  1472.         model.foreach (maybe_select)
  1473.  
  1474.         if (self.printer != None and
  1475.             self.printer.name not in self.printers.keys ()):
  1476.             # The printer we're editing has been deleted.
  1477.             self.PrinterPropertiesDialog.response (gtk.RESPONSE_CANCEL)
  1478.  
  1479.     # Connect to Server
  1480.  
  1481.     def on_connect_servername_changed(self, widget):
  1482.         self.btnConnect.set_sensitive (len (widget.get_active_text ()) > 0)
  1483.  
  1484.     def on_connect_activate(self, widget):
  1485.         # Use browsed queues to build up a list of known IPP servers
  1486.         servers = self.getServers()
  1487.         current_server = (self.printer and self.printer.getServer()) \
  1488.                          or cups.getServer()
  1489.  
  1490.         store = gtk.ListStore (gobject.TYPE_STRING)
  1491.         self.cmbServername.set_model(store)
  1492.         for server in servers:
  1493.             self.cmbServername.append_text(server)
  1494.         self.cmbServername.show()
  1495.  
  1496.         self.cmbServername.child.set_text (current_server)
  1497.         self.chkEncrypted.set_active (cups.getEncryption() ==
  1498.                                       cups.HTTP_ENCRYPT_ALWAYS)
  1499.  
  1500.         self.cmbServername.child.set_activates_default (True)
  1501.         self.cmbServername.grab_focus ()
  1502.         self.ConnectDialog.set_transient_for (self.PrintersWindow)
  1503.         response = self.ConnectDialog.run()
  1504.  
  1505.         self.ConnectDialog.hide()
  1506.  
  1507.         if response != gtk.RESPONSE_OK:
  1508.             return
  1509.  
  1510.         if self.chkEncrypted.get_active():
  1511.             cups.setEncryption(cups.HTTP_ENCRYPT_ALWAYS)
  1512.         else:
  1513.             cups.setEncryption(cups.HTTP_ENCRYPT_IF_REQUESTED)
  1514.         self.connect_encrypt = cups.getEncryption ()
  1515.  
  1516.         servername = self.cmbServername.child.get_text()
  1517.  
  1518.         self.lblConnecting.set_markup(_("<i>Opening connection to %s</i>") %
  1519.                                       servername)
  1520.         self.newPrinterGUI.dropPPDs()
  1521.         self.ConnectingDialog.set_transient_for(self.PrintersWindow)
  1522.         self.ConnectingDialog.show()
  1523.         gobject.timeout_add (40, self.update_connecting_pbar)
  1524.         self.connect_server = servername
  1525.         # We need to set the connecting user in this thread as well.
  1526.         cups.setServer(self.connect_server)
  1527.         cups.setUser('')
  1528.         self.connect_user = cups.getUser()
  1529.         # Now start a new thread for connection.
  1530.         self.connect_thread = thread.start_new_thread(self.connect,
  1531.                                                       (self.PrintersWindow,))
  1532.  
  1533.     def update_connecting_pbar (self):
  1534.         if not self.ConnectingDialog.get_property ("visible"):
  1535.             return False # stop animation
  1536.  
  1537.         self.pbarConnecting.pulse ()
  1538.         return True
  1539.  
  1540.     def on_connectingdialog_delete (self, widget, event):
  1541.         self.on_cancel_connect_clicked (widget)
  1542.         return True
  1543.  
  1544.     def on_cancel_connect_clicked(self, widget):
  1545.         """
  1546.         Stop connection to new server
  1547.         (Doesn't really stop but sets flag for the connecting thread to
  1548.         ignore the connection)
  1549.         """
  1550.         self.connect_thread = None
  1551.         self.ConnectingDialog.hide()
  1552.  
  1553.     def connect(self, parent=None):
  1554.         """
  1555.         Open a connection to a new server. Is executed in a separate thread!
  1556.         """
  1557.         cups.setUser(self.connect_user)
  1558.         if self.connect_server[0] == '/':
  1559.             # UNIX domain socket.  This may potentially fail if the server
  1560.             # settings have been changed and cupsd has written out a
  1561.             # configuration that does not include a Listen line for the
  1562.             # UNIX domain socket.  To handle this special case, try to
  1563.             # connect once and fall back to "localhost" on failure.
  1564.             try:
  1565.                 connection = cups.Connection (host=self.connect_server,
  1566.                                               encryption=self.connect_encrypt)
  1567.  
  1568.                 # Worked fine.  Disconnect, and we'll connect for real
  1569.                 # shortly.
  1570.                 del connection
  1571.             except RuntimeError:
  1572.                 # When we connect, avoid the domain socket.
  1573.                 cups.setServer ("localhost")
  1574.             except:
  1575.                 nonfatalException ()
  1576.  
  1577.         try:
  1578.             connection = authconn.Connection(parent,
  1579.                                              host=self.connect_server,
  1580.                                              encryption=self.connect_encrypt)
  1581.             self.newPrinterGUI.dropPPDs ()
  1582.         except RuntimeError, s:
  1583.             if self.connect_thread != thread.get_ident(): return
  1584.             gtk.gdk.threads_enter()
  1585.             self.ConnectingDialog.hide()
  1586.             show_IPP_Error(None, s, parent)
  1587.             gtk.gdk.threads_leave()
  1588.             return
  1589.         except cups.IPPError, (e, s):
  1590.             if self.connect_thread != thread.get_ident(): return
  1591.             gtk.gdk.threads_enter()
  1592.             self.ConnectingDialog.hide()
  1593.             show_IPP_Error(e, s, parent)
  1594.             gtk.gdk.threads_leave()
  1595.             return
  1596.         except:
  1597.             nonfatalException ()
  1598.  
  1599.         if self.connect_thread != thread.get_ident(): return
  1600.         gtk.gdk.threads_enter()
  1601.  
  1602.         try:
  1603.             self.ConnectingDialog.hide()
  1604.             self.cups = connection
  1605.             self.setConnected()
  1606.             self.populateList()
  1607.     except cups.HTTPError, (s,):
  1608.             self.cups = None
  1609.             self.setConnected()
  1610.             self.populateList()
  1611.             show_HTTP_Error(s, parent)
  1612.         except:
  1613.             nonfatalException ()
  1614.  
  1615.         gtk.gdk.threads_leave()
  1616.  
  1617.     def reconnect (self):
  1618.         """Reconnect to CUPS after the server has reloaded."""
  1619.         # libcups would handle the reconnection if we just told it to
  1620.         # do something, for example fetching a list of classes.
  1621.         # However, our local authentication certificate would be
  1622.         # invalidated by a server restart, so it is better for us to
  1623.         # handle the reconnection ourselves.
  1624.  
  1625.         attempt = 1
  1626.         while attempt <= 5:
  1627.             try:
  1628.                 time.sleep(1)
  1629.                 self.cups._connect ()
  1630.                 break
  1631.             except RuntimeError:
  1632.                 # Connection failed.
  1633.                 attempt += 1
  1634.  
  1635.     def on_btnCancelConnect_clicked(self, widget):
  1636.         """Close Connect dialog"""
  1637.         self.ConnectWindow.hide()
  1638.  
  1639.     # refresh
  1640.  
  1641.     def on_btnRefresh_clicked(self, button):
  1642.         if self.cups == None:
  1643.             try:
  1644.                 self.cups = authconn.Connection(self.PrintersWindow)
  1645.             except RuntimeError:
  1646.                 pass
  1647.  
  1648.             self.setConnected()
  1649.  
  1650.         self.populateList()
  1651.  
  1652.     # Data handling
  1653.  
  1654.     def on_printer_changed(self, widget):
  1655.         if isinstance(widget, gtk.CheckButton):
  1656.             value = widget.get_active()
  1657.         elif isinstance(widget, gtk.Entry):
  1658.             value = widget.get_text()
  1659.         elif isinstance(widget, gtk.RadioButton):
  1660.             value = widget.get_active()
  1661.         elif isinstance(widget, gtk.ComboBox):
  1662.             model = widget.get_model ()
  1663.             iter = widget.get_active_iter()
  1664.             value = model.get_value (iter, 1)
  1665.         else:
  1666.             raise ValueError, "Widget type not supported (yet)"
  1667.  
  1668.         p = self.printer
  1669.         old_values = {
  1670.             self.entPDescription : p.info,
  1671.             self.entPLocation : p.location,
  1672.             self.entPDevice : p.device_uri,
  1673.             self.chkPEnabled : p.enabled,
  1674.             self.chkPAccepting : not p.rejecting,
  1675.             self.chkPShared : p.is_shared,
  1676.             self.cmbPStartBanner : p.job_sheet_start,
  1677.             self.cmbPEndBanner : p.job_sheet_end,
  1678.             self.cmbPErrorPolicy : p.error_policy,
  1679.             self.cmbPOperationPolicy : p.op_policy,
  1680.             self.rbtnPAllow: p.default_allow,
  1681.             }
  1682.  
  1683.         old_value = old_values[widget]
  1684.  
  1685.         if old_value == value:
  1686.             self.changed.discard(widget)
  1687.         else:
  1688.             self.changed.add(widget)
  1689.         self.setDataButtonState()
  1690.  
  1691.     def option_changed(self, option):
  1692.         if option.is_changed():
  1693.             self.changed.add(option)
  1694.         else:
  1695.             self.changed.discard(option)
  1696.  
  1697.         if option.conflicts:
  1698.             self.conflicts.add(option)
  1699.         else:
  1700.             self.conflicts.discard(option)
  1701.         self.setDataButtonState()
  1702.  
  1703.         if (self.option_manualfeed and self.option_inputslot and
  1704.             option == self.option_manualfeed):
  1705.             if option.get_current_value() == "True":
  1706.                 self.option_inputslot.disable ()
  1707.             else:
  1708.                 self.option_inputslot.enable ()
  1709.  
  1710.     # Access control
  1711.     def getPUsers(self):
  1712.         """return list of usernames from the GUI"""
  1713.         model = self.tvPUsers.get_model()
  1714.         result = []
  1715.         model.foreach(lambda model, path, iter:
  1716.                       result.append(model.get(iter, 0)[0]))
  1717.         result.sort()
  1718.         return result
  1719.  
  1720.     def setPUsers(self, users):
  1721.         """write list of usernames inot the GUI"""
  1722.         model = self.tvPUsers.get_model()
  1723.         model.clear()
  1724.         for user in users:
  1725.             model.append((user,))
  1726.  
  1727.         self.on_entPUser_changed(self.entPUser)
  1728.         self.on_tvPUsers_cursor_changed(self.tvPUsers)
  1729.  
  1730.     def checkPUsersChanged(self):
  1731.         """check if users in GUI and printer are different
  1732.         and set self.changed"""
  1733.         if self.getPUsers() != self.printer.except_users:
  1734.             self.changed.add(self.tvPUsers)
  1735.         else:
  1736.             self.changed.discard(self.tvPUsers)
  1737.  
  1738.         self.on_tvPUsers_cursor_changed(self.tvPUsers)
  1739.         self.setDataButtonState()
  1740.  
  1741.     def on_btnPAddUser_clicked(self, button):
  1742.         user = self.entPUser.get_text()
  1743.         if user:
  1744.             self.tvPUsers.get_model().insert(0, (user,))
  1745.             self.entPUser.set_text("")
  1746.         self.checkPUsersChanged()
  1747.  
  1748.     def on_btnPDelUser_clicked(self, button):
  1749.         model, rows = self.tvPUsers.get_selection().get_selected_rows()
  1750.         rows = [gtk.TreeRowReference(model, row) for row in rows]
  1751.         for row in rows:
  1752.             path = row.get_path()
  1753.             iter = model.get_iter(path)
  1754.             model.remove(iter)
  1755.         self.checkPUsersChanged()
  1756.  
  1757.     def on_entPUser_changed(self, widget):
  1758.         self.btnPAddUser.set_sensitive(bool(widget.get_text()))
  1759.  
  1760.     def on_tvPUsers_cursor_changed(self, widget):
  1761.         model, rows = widget.get_selection().get_selected_rows()
  1762.         self.btnPDelUser.set_sensitive(bool(rows))
  1763.  
  1764.     # Server side options
  1765.     def on_job_option_reset(self, button):
  1766.         option = self.job_options_buttons[button]
  1767.         option.reset ()
  1768.         # Remember to set this option for removal in the IPP request.
  1769.         if self.server_side_options.has_key (option.name):
  1770.             del self.server_side_options[option.name]
  1771.         if option.is_changed ():
  1772.             self.changed.add(option)
  1773.         else:
  1774.             self.changed.discard(option)
  1775.         self.setDataButtonState()
  1776.  
  1777.     def on_job_option_changed(self, widget):
  1778.         if not self.printer:
  1779.             return
  1780.         option = self.job_options_widgets[widget]
  1781.         option.changed ()
  1782.         if option.is_changed ():
  1783.             self.server_side_options[option.name] = option
  1784.             self.changed.add(option)
  1785.         else:
  1786.             if self.server_side_options.has_key (option.name):
  1787.                 del self.server_side_options[option.name]
  1788.             self.changed.discard(option)
  1789.         self.setDataButtonState()
  1790.         # Don't set the reset button insensitive if the option hasn't
  1791.         # changed from the original value: it's still meaningful to
  1792.         # reset the option to the system default.
  1793.  
  1794.     def draw_other_job_options (self, editable=True):
  1795.         n = len (self.other_job_options)
  1796.         if n == 0:
  1797.             self.tblJOOther.hide_all ()
  1798.             return
  1799.  
  1800.         self.tblJOOther.resize (n, 3)
  1801.         children = self.tblJOOther.get_children ()
  1802.         for child in children:
  1803.             self.tblJOOther.remove (child)
  1804.         i = 0
  1805.         for opt in self.other_job_options:
  1806.             self.tblJOOther.attach (opt.label, 0, 1, i, i + 1,
  1807.                                     xoptions=gtk.FILL,
  1808.                                     yoptions=gtk.FILL)
  1809.             opt.label.set_alignment (0.0, 0.5)
  1810.             self.tblJOOther.attach (opt.selector, 1, 2, i, i + 1,
  1811.                                     xoptions=gtk.FILL,
  1812.                                     yoptions=0)
  1813.             opt.selector.set_sensitive (editable)
  1814.  
  1815.             btn = gtk.Button(stock="gtk-remove")
  1816.             btn.connect("clicked", self.on_btnJOOtherRemove_clicked)
  1817.             btn.set_data("pyobject", opt)
  1818.             btn.set_sensitive (editable)
  1819.             self.tblJOOther.attach(btn, 2, 3, i, i + 1,
  1820.                                    xoptions=0,
  1821.                                    yoptions=0)
  1822.             i += 1
  1823.  
  1824.         self.tblJOOther.show_all ()
  1825.  
  1826.     def add_job_option(self, name, value = "", supported = "", is_new=True,
  1827.                        editable=True):
  1828.         option = options.OptionWidget(name, value, supported,
  1829.                                       self.option_changed)
  1830.         option.is_new = is_new
  1831.         self.other_job_options.append (option)
  1832.         self.draw_other_job_options (editable=editable)
  1833.         self.server_side_options[name] = option
  1834.         if name in self.changed: # was deleted before
  1835.             option.is_new = False
  1836.         self.changed.add(option)
  1837.         self.setDataButtonState()
  1838.         if is_new:
  1839.             option.selector.grab_focus ()
  1840.  
  1841.     def on_btnJOOtherRemove_clicked(self, button):
  1842.         option = button.get_data("pyobject")
  1843.         self.other_job_options.remove (option)
  1844.         self.draw_other_job_options ()
  1845.         if option.is_new:
  1846.             self.changed.discard(option)
  1847.         else:
  1848.             # keep name as reminder that option got deleted
  1849.             self.changed.add(option.name)
  1850.         del self.server_side_options[option.name]
  1851.         self.setDataButtonState()
  1852.  
  1853.     def on_btnNewJobOption_clicked(self, button):
  1854.         name = self.entNewJobOption.get_text()
  1855.         self.add_job_option(name)
  1856.         self.tblJOOther.show_all()
  1857.         self.entNewJobOption.set_text ('')
  1858.         self.btnNewJobOption.set_sensitive (False)
  1859.         self.setDataButtonState()
  1860.  
  1861.     def on_entNewJobOption_changed(self, widget):
  1862.         text = self.entNewJobOption.get_text()
  1863.         active = (len(text) > 0) and text not in self.server_side_options
  1864.         self.btnNewJobOption.set_sensitive(active)
  1865.  
  1866.     def on_entNewJobOption_activate(self, widget):
  1867.         self.on_btnNewJobOption_clicked (widget) # wrong widget but ok
  1868.  
  1869.     # set buttons sensitivity
  1870.     def setDataButtonState(self):
  1871.         try: # Might not be a printer selected
  1872.             possible = (self.ppd and
  1873.                         not bool (self.changed) and
  1874.                         self.printer.enabled and
  1875.                         not self.printer.rejecting)
  1876.  
  1877.             self.btnPrintTestPage.set_sensitive (possible)
  1878.  
  1879.             commands = (self.printer.type & cups.CUPS_PRINTER_COMMANDS) != 0
  1880.             self.btnSelfTest.set_sensitive (commands and possible)
  1881.             self.btnCleanHeads.set_sensitive (commands and possible)
  1882.         except:
  1883.             pass
  1884.  
  1885.         installablebold = False
  1886.         optionsbold = False
  1887.         if self.conflicts:
  1888.             debugprint ("Conflicts detected")
  1889.             self.btnConflict.show()
  1890.             for option in self.conflicts:
  1891.                 if option.tab_label == self.lblPInstallOptions:
  1892.                     installablebold = True
  1893.                 else:
  1894.                     optionsbold = True
  1895.         else:
  1896.             self.btnConflict.hide()
  1897.         installabletext = _("Installable Options")
  1898.         optionstext = _("Printer Options")
  1899.         if installablebold:
  1900.             installabletext = "<b>%s</b>" % installabletext
  1901.         if optionsbold:
  1902.             optionstext = "<b>%s</b>" % optionstext
  1903.         self.lblPInstallOptions.set_markup (installabletext)
  1904.         self.lblPOptions.set_markup (optionstext)
  1905.  
  1906.         store = self.tvPrinterProperties.get_model ()
  1907.         if store:
  1908.             for n in range (self.ntbkPrinter.get_n_pages ()):
  1909.                 page = self.ntbkPrinter.get_nth_page (n)
  1910.                 label = self.ntbkPrinter.get_tab_label (page)
  1911.                 try:
  1912.                     if label == self.lblPInstallOptions:
  1913.                         iter = store.get_iter ((n,))
  1914.                         store.set_value (iter, 0, installabletext)
  1915.                     elif label == self.lblPOptions:
  1916.                         iter = store.get_iter ((n,))
  1917.                         store.set_value (iter, 0, optionstext)
  1918.                 except ValueError:
  1919.                     # If we get here, the store has not yet been set
  1920.                     # up (trac #111).
  1921.                     pass
  1922.  
  1923.         self.btnPrinterPropertiesApply.set_sensitive (len (self.changed) > 0 and
  1924.                                                       not self.conflicts)
  1925.         self.btnPrinterPropertiesOK.set_sensitive (not self.conflicts)
  1926.  
  1927.     def save_printer(self, printer, saveall=False, parent=None):
  1928.         if parent == None:
  1929.             parent = self.PrinterPropertiesDialog
  1930.         class_deleted = False
  1931.         name = printer.name
  1932.  
  1933.         if printer.is_class:
  1934.             self.cups._begin_operation (_("modifying class %s") % name)
  1935.         else:
  1936.             self.cups._begin_operation (_("modifying printer %s") % name)
  1937.  
  1938.         try:
  1939.             if not printer.is_class and self.ppd:
  1940.                 self.getPrinterSettings()
  1941.                 if self.ppd.nondefaultsMarked() or saveall:
  1942.                     self.cups.addPrinter(name, ppd=self.ppd)
  1943.  
  1944.             if printer.is_class:
  1945.                 # update member list
  1946.                 new_members = getCurrentClassMembers(self.tvClassMembers)
  1947.                 if not new_members:
  1948.                     dialog = gtk.MessageDialog(
  1949.                         flags=0, type=gtk.MESSAGE_WARNING,
  1950.                         buttons=gtk.BUTTONS_YES_NO,
  1951.                         message_format=_("This will delete this class!"))
  1952.                     dialog.format_secondary_text(_("Proceed anyway?"))
  1953.                     result = dialog.run()
  1954.                     dialog.destroy()
  1955.                     if result==gtk.RESPONSE_NO:
  1956.                         self.cups._end_operation ()
  1957.                         return True
  1958.                     class_deleted = True
  1959.  
  1960.                 # update member list
  1961.                 old_members = printer.class_members[:]
  1962.  
  1963.                 for member in new_members:
  1964.                     if member in old_members:
  1965.                         old_members.remove(member)
  1966.                     else:
  1967.                         self.cups.addPrinterToClass(member, name)
  1968.                 for member in old_members:
  1969.                     self.cups.deletePrinterFromClass(member, name)
  1970.  
  1971.             location = self.entPLocation.get_text()
  1972.             info = self.entPDescription.get_text()
  1973.             device_uri = self.entPDevice.get_text()
  1974.  
  1975.             enabled = self.chkPEnabled.get_active()
  1976.             accepting = self.chkPAccepting.get_active()
  1977.             shared = self.chkPShared.get_active()
  1978.  
  1979.             if info!=printer.info or saveall:
  1980.                 self.cups.setPrinterInfo(name, info)
  1981.             if location!=printer.location or saveall:
  1982.                 self.cups.setPrinterLocation(name, location)
  1983.             if (not printer.is_class and
  1984.                 (device_uri!=printer.device_uri or saveall)):
  1985.                 self.cups.setPrinterDevice(name, device_uri)
  1986.  
  1987.             if enabled != printer.enabled or saveall:
  1988.                 self.printer.setEnabled(enabled)
  1989.             if accepting == printer.rejecting or saveall:
  1990.                 self.printer.setAccepting(accepting)
  1991.             if shared != printer.is_shared or saveall:
  1992.                 self.printer.setShared(shared)
  1993.  
  1994.             def get_combo_value (cmb):
  1995.                 model = cmb.get_model ()
  1996.                 iter = cmb.get_active_iter ()
  1997.                 return model.get_value (iter, 1)
  1998.  
  1999.             job_sheet_start = get_combo_value (self.cmbPStartBanner)
  2000.             job_sheet_end = get_combo_value (self.cmbPEndBanner)
  2001.             error_policy = get_combo_value (self.cmbPErrorPolicy)
  2002.             op_policy = get_combo_value (self.cmbPOperationPolicy)
  2003.  
  2004.             if (job_sheet_start != printer.job_sheet_start or
  2005.                 job_sheet_end != printer.job_sheet_end) or saveall:
  2006.                 printer.setJobSheets(job_sheet_start, job_sheet_end)
  2007.             if error_policy != printer.error_policy or saveall:
  2008.                 printer.setErrorPolicy(error_policy)
  2009.             if op_policy != printer.op_policy or saveall:
  2010.                 printer.setOperationPolicy(op_policy)
  2011.  
  2012.             default_allow = self.rbtnPAllow.get_active()
  2013.             except_users = self.getPUsers()
  2014.  
  2015.             if (default_allow != printer.default_allow or
  2016.                 except_users != printer.except_users) or saveall:
  2017.                 printer.setAccess(default_allow, except_users)
  2018.  
  2019.             for option in printer.attributes:
  2020.                 if option not in self.server_side_options:
  2021.                     printer.unsetOption(option)
  2022.             for option in self.server_side_options.itervalues():
  2023.                 if (option.is_changed() or
  2024.                     saveall and
  2025.                     option.get_current_value () != option.system_default):
  2026.                     printer.setOption(option.name, option.get_current_value())
  2027.  
  2028.         except cups.IPPError, (e, s):
  2029.             show_IPP_Error(e, s, parent)
  2030.             self.cups._end_operation ()
  2031.             return True
  2032.         self.cups._end_operation ()
  2033.         self.changed = set() # of options
  2034.  
  2035.         if not self.cups._use_pk and not self.__dict__.has_key ("server_settings"):
  2036.             # We can authenticate with the server correctly at this point,
  2037.             # but we have never fetched the server settings to see whether
  2038.             # the server is publishing shared printers.  Fetch the settings
  2039.             # now so that we can update the "not published" label if necessary.
  2040.             self.cups._begin_operation (_("fetching server settings"))
  2041.             try:
  2042.                 self.server_settings = self.cups.adminGetServerSettings()
  2043.             except:
  2044.                 nonfatalException()
  2045.  
  2046.             self.cups._end_operation ()
  2047.  
  2048.         if class_deleted:
  2049.             self.monitor.update ()
  2050.         else:
  2051.             # Update our copy of the printer's settings.
  2052.             self.cups._begin_operation (_("obtaining queue details"))
  2053.             try:
  2054.                 printers = cupshelpers.getPrinters (self.cups)
  2055.                 this_printer = { name: printers[name] }
  2056.                 self.printers.update (this_printer)
  2057.             except cups.IPPError, (e, s):
  2058.                 show_IPP_Error(e, s, self.PrinterPropertiesDialog)
  2059.             except KeyError:
  2060.                 # The printer was deleted in the mean time and the
  2061.                 # user made no changes.
  2062.                 self.populateList ()
  2063.  
  2064.             self.cups._end_operation ()
  2065.         return False
  2066.  
  2067.     def getPrinterSettings(self):
  2068.         #self.ppd.markDefaults()
  2069.         for option in self.options.itervalues():
  2070.             option.writeback()
  2071.  
  2072.     ### Printer Properties tree view signal handlers
  2073.     def on_tvPrinterProperties_selection_changed (self, selection):
  2074.         # Prevent selection from being de-selected.
  2075.         (model, iter) = selection.get_selected ()
  2076.         if iter:
  2077.             self.printer_properties_last_iter_selected = iter
  2078.         else:
  2079.             try:
  2080.                 iter = self.printer_properties_last_iter_selected
  2081.             except AttributeError:
  2082.                 # Not set yet.
  2083.                 return
  2084.  
  2085.             if model.iter_is_valid (iter):
  2086.                 selection.select_iter (iter)
  2087.  
  2088.     def on_tvPrinterProperties_cursor_changed (self, treeview):
  2089.         # Adjust notebook to reflect selected item.
  2090.         (path, column) = treeview.get_cursor ()
  2091.         if path != None:
  2092.             model = treeview.get_model ()
  2093.             iter = model.get_iter (path)
  2094.             n = model.get_value (iter, 1)
  2095.             self.ntbkPrinter.set_current_page (n)
  2096.  
  2097.     # set default printer
  2098.     def set_system_or_user_default_printer (self, name):
  2099.         # First, decide if this is already the system default, in which
  2100.         # case we only need to clear the user default.
  2101.         userdef = userdefault.UserDefaultPrinter ()
  2102.         if name == self.default_printer:
  2103.             userdef.clear ()
  2104.             self.populateList ()
  2105.             return
  2106.  
  2107.         userdefault.UserDefaultPrompt (self.set_default_printer,
  2108.                                        self.populateList,
  2109.                                        name,
  2110.                                        _("Set Default Printer"),
  2111.                                        self.PrintersWindow,
  2112.                                        _("Do you want to set this as "
  2113.                                          "the system-wide default printer?"),
  2114.                                        _("Set as the _system-wide "
  2115.                                          "default printer"),
  2116.                                        _("_Clear my personal default setting"),
  2117.                                        _("Set as my _personal default printer"))
  2118.  
  2119.     def set_default_printer (self, name):
  2120.         printer = self.printers[name]
  2121.         reload = False
  2122.         self.cups._begin_operation (_("setting default printer"))
  2123.         try:
  2124.             reload = printer.setAsDefault ()
  2125.         except cups.HTTPError, (s,):
  2126.             show_HTTP_Error (s, self.PrintersWindow)
  2127.             self.cups._end_operation ()
  2128.             return
  2129.         except cups.IPPError, (e, msg):
  2130.             show_IPP_Error(e, msg, self.PrintersWindow)
  2131.             self.cups._end_operation ()
  2132.             return
  2133.  
  2134.         self.cups._end_operation ()
  2135.  
  2136.         # Now reconnect in case the server needed to reload.  This may
  2137.         # happen if we replaced the lpoptions file.
  2138.         if reload:
  2139.             self.reconnect ()
  2140.  
  2141.         try:
  2142.             self.populateList()
  2143.         except cups.HTTPError, (s,):
  2144.             self.cups = None
  2145.             self.setConnected()
  2146.             self.populateList()
  2147.             show_HTTP_Error(s, self.PrintersWindow)
  2148.  
  2149.     # print test page
  2150.  
  2151.     def on_btnPrintTestPage_clicked(self, button):
  2152.         if self.ppd == False:
  2153.             # Can't print a test page for a raw queue.
  2154.             return
  2155.  
  2156.         # if we have a page size specific custom test page, use it;
  2157.         # otherwise use cups' default one
  2158.         custom_testpage = None
  2159.         if self.ppd != False:
  2160.             opt = self.ppd.findOption ("PageSize")
  2161.             if opt:
  2162.                 custom_testpage = os.path.join(pkgdata,
  2163.                                                'testpage-%s.ps' %
  2164.                                                opt.defchoice.lower())
  2165.  
  2166.         # Connect as the current user so that the test page can be managed
  2167.         # as a normal job.
  2168.         user = cups.getUser ()
  2169.         cups.setUser ('')
  2170.         try:
  2171.             c = authconn.Connection (self.PrintersWindow, try_as_root=False,
  2172.                                      host=self.connect_server,
  2173.                                      encryption=self.connect_encrypt)
  2174.         except RuntimeError, s:
  2175.             show_IPP_Error (None, s, self.PrintersWindow)
  2176.             return
  2177.  
  2178.         job_id = None
  2179.         c._begin_operation (_("printing test page"))
  2180.         try:
  2181.             if custom_testpage and os.path.exists(custom_testpage):
  2182.                 debugprint ('Printing custom test page ' + custom_testpage)
  2183.                 job_id = c.printTestPage(self.printer.name,
  2184.                                          file=custom_testpage)
  2185.             else:
  2186.                 debugprint ('Printing default test page')
  2187.                 job_id = c.printTestPage(self.printer.name)
  2188.         except cups.IPPError, (e, msg):
  2189.             if (e == cups.IPP_NOT_AUTHORIZED and
  2190.                 self.connect_server != 'localhost' and
  2191.                 self.connect_server[0] != '/'):
  2192.                 show_error_dialog (_("Not possible"),
  2193.                                    _("The remote server did not accept "
  2194.                                      "the print job, most likely "
  2195.                                      "because the printer is not "
  2196.                                      "shared."),
  2197.                                    self.PrintersWindow)
  2198.             else:
  2199.                 show_IPP_Error(e, msg, self.PrintersWindow)
  2200.  
  2201.         c._end_operation ()
  2202.         cups.setUser (user)
  2203.  
  2204.         if job_id != None:
  2205.             show_info_dialog (_("Submitted"),
  2206.                               _("Test page submitted as job %d") % job_id,
  2207.                               parent=self.PrintersWindow)
  2208.  
  2209.     def maintenance_command (self, command):
  2210.         (tmpfd, tmpfname) = tempfile.mkstemp ()
  2211.         os.write (tmpfd, "#CUPS-COMMAND\n%s\n" % command)
  2212.         os.close (tmpfd)
  2213.         self.cups._begin_operation (_("sending maintenance command"))
  2214.         try:
  2215.             format = "application/vnd.cups-command"
  2216.             job_id = self.cups.printTestPage (self.printer.name,
  2217.                                               format=format,
  2218.                                               file=tmpfname,
  2219.                                               user=self.connect_user)
  2220.             show_info_dialog (_("Submitted"),
  2221.                               _("Maintenance command submitted as "
  2222.                                 "job %d") % job_id,
  2223.                               parent=self.PrintersWindow)
  2224.         except cups.IPPError, (e, msg):
  2225.             if (e == cups.IPP_NOT_AUTHORIZED and
  2226.                 self.printer.name != 'localhost'):
  2227.                 show_error_dialog (_("Not possible"),
  2228.                                    _("The remote server did not accept "
  2229.                                      "the print job, most likely "
  2230.                                      "because the printer is not "
  2231.                                      "shared."),
  2232.                                    self.PrintersWindow)
  2233.             else:
  2234.                 show_IPP_Error(e, msg, self.PrintersWindow)
  2235.  
  2236.         self.cups._end_operation ()
  2237.  
  2238.         os.unlink (tmpfname)
  2239.  
  2240.     def on_btnSelfTest_clicked(self, button):
  2241.         self.maintenance_command ("PrintSelfTestPage")
  2242.  
  2243.     def on_btnCleanHeads_clicked(self, button):
  2244.         self.maintenance_command ("Clean all")
  2245.  
  2246.     def fillComboBox(self, combobox, values, value, translationdict=None):
  2247.         if translationdict == None:
  2248.             translationdict = ppdippstr.TranslactionDict ()
  2249.  
  2250.         model = gtk.ListStore (gobject.TYPE_STRING,
  2251.                                gobject.TYPE_STRING)
  2252.         combobox.set_model (model)
  2253.         set_active = False
  2254.         for nr, val in enumerate(values):
  2255.             model.append ([(translationdict.get (val)), val])
  2256.             if val == value:
  2257.                 combobox.set_active(nr)
  2258.                 set_active = True
  2259.  
  2260.         if not set_active:
  2261.             combobox.set_active (0)
  2262.  
  2263.     def fillPrinterTab(self, name):
  2264.         self.changed = set() # of options
  2265.         self.options = {} # keyword -> Option object
  2266.         self.conflicts = set() # of options
  2267.  
  2268.         printer = self.printers[name]
  2269.         self.printer = printer
  2270.         printer.getAttributes ()
  2271.         try:
  2272.             # CUPS 1.4
  2273.             publishing = printer.other_attributes['server-is-sharing-printers']
  2274.             self.server_is_publishing = publishing
  2275.         except KeyError:
  2276.             pass
  2277.  
  2278.         editable = not self.printer.discovered
  2279.         editablePPD = not self.printer.remote
  2280.  
  2281.         try:
  2282.             self.ppd = printer.getPPD()
  2283.             self.ppd_local = printer.getPPD()
  2284.             if self.ppd_local != False:
  2285.                 self.ppd_local.localize()
  2286.         except cups.IPPError, (e, m):
  2287.             # Some IPP error other than IPP_NOT_FOUND.
  2288.             show_IPP_Error(e, m, self.PrintersWindow)
  2289.             # Treat it as a raw queue.
  2290.             self.ppd = False
  2291.         except RuntimeError:
  2292.             # The underlying cupsGetPPD2() function returned NULL without
  2293.             # setting an IPP error, so it'll be something like a failed
  2294.             # connection.
  2295.             show_error_dialog (_("Error"),
  2296.                                _("There was a problem connecting to "
  2297.                                  "the CUPS server."),
  2298.                                self.PrintersWindow)
  2299.             raise
  2300.  
  2301.         for widget in (self.entPDescription, self.entPLocation,
  2302.                        self.entPDevice):
  2303.             widget.set_editable(editable)
  2304.  
  2305.         for widget in (self.btnSelectDevice, self.btnChangePPD,
  2306.                        self.chkPEnabled, self.chkPAccepting, self.chkPShared,
  2307.                        self.cmbPStartBanner, self.cmbPEndBanner,
  2308.                        self.cmbPErrorPolicy, self.cmbPOperationPolicy,
  2309.                        self.rbtnPAllow, self.rbtnPDeny, self.tvPUsers,
  2310.                        self.entPUser, self.btnPAddUser, self.btnPDelUser):
  2311.             widget.set_sensitive(editable)
  2312.  
  2313.         # Description page
  2314.         self.entPDescription.set_text(printer.info)
  2315.         self.entPLocation.set_text(printer.location)
  2316.  
  2317.         uri = printer.device_uri
  2318.         self.entPDevice.set_text(uri)
  2319.         self.changed.discard(self.entPDevice)
  2320.  
  2321.         # Hide make/model and Device URI for classes
  2322.         for widget in (self.lblPMakeModel2, self.lblPMakeModel,
  2323.                        self.btnChangePPD, self.lblPDevice2,
  2324.                        self.entPDevice, self.btnSelectDevice):
  2325.             if printer.is_class:
  2326.                 widget.hide()
  2327.             else:
  2328.                 widget.show()
  2329.  
  2330.  
  2331.         # Policy tab
  2332.         # ----------
  2333.  
  2334.         try:
  2335.             if printer.is_shared:
  2336.                 if self.server_is_publishing:
  2337.                     self.lblNotPublished.hide_all ()
  2338.                 else:
  2339.                     self.lblNotPublished.show_all ()
  2340.             else:
  2341.                 self.lblNotPublished.hide_all ()
  2342.         except:
  2343.             nonfatalException()
  2344.             self.lblNotPublished.hide_all ()
  2345.  
  2346.         # Job sheets
  2347.         self.cmbPStartBanner.set_sensitive(editable)
  2348.         self.cmbPEndBanner.set_sensitive(editable)
  2349.  
  2350.         # Policies
  2351.         self.cmbPErrorPolicy.set_sensitive(editable)
  2352.         self.cmbPOperationPolicy.set_sensitive(editable)
  2353.  
  2354.         # Access control
  2355.         self.entPUser.set_text("")
  2356.  
  2357.         # Server side options (Job options)
  2358.         self.server_side_options = {}
  2359.         for option in self.job_options_widgets.values ():
  2360.             if option.name == "media" and self.ppd:
  2361.                 # Slightly special case because the 'system default'
  2362.                 # (i.e. what you get when you press Reset) depends
  2363.                 # on the printer's PageSize.
  2364.                 opt = self.ppd.findOption ("PageSize")
  2365.                 if opt:
  2366.                     option.set_default (opt.defchoice)
  2367.  
  2368.             option_editable = editable
  2369.             try:
  2370.                 value = self.printer.attributes[option.name]
  2371.             except KeyError:
  2372.                 option.reinit (None)
  2373.             else:
  2374.                 try:
  2375.                     if self.printer.possible_attributes.has_key (option.name):
  2376.                         supported = self.printer.\
  2377.                                     possible_attributes[option.name][1]
  2378.                         # Set the option widget.
  2379.                         # In CUPS 1.3.x the orientation-requested-default
  2380.                         # attribute may have the value None; this means there
  2381.                         # is no value set.  This suits our needs here, as None
  2382.                         # resets the option to the system default and makes the
  2383.                         # Reset button insensitive.
  2384.                         option.reinit (value, supported=supported)
  2385.                     else:
  2386.                         option.reinit (value)
  2387.  
  2388.                     self.server_side_options[option.name] = option
  2389.                 except:
  2390.                     option_editable = False
  2391.                     show_error_dialog (_("Error"),
  2392.                                        _("Option '%s' has value '%s' "
  2393.                                          "and cannot be edited.") %
  2394.                                        (option.name, value),
  2395.                                        self.PrintersWindow)
  2396.             option.widget.set_sensitive (option_editable)
  2397.             if not editable:
  2398.                 option.button.set_sensitive (False)
  2399.         self.other_job_options = []
  2400.         self.draw_other_job_options (editable=editable)
  2401.         for option in self.printer.attributes.keys ():
  2402.             if self.server_side_options.has_key (option):
  2403.                 continue
  2404.             value = self.printer.attributes[option]
  2405.             if self.printer.possible_attributes.has_key (option):
  2406.                 supported = self.printer.possible_attributes[option][1]
  2407.             else:
  2408.                 if isinstance (value, bool):
  2409.                     supported = ["true", "false"]
  2410.                     value = str (value).lower ()
  2411.                 else:
  2412.                     supported = ""
  2413.                     value = str (value)
  2414.  
  2415.             self.add_job_option (option, value=value,
  2416.                                  supported=supported, is_new=False,
  2417.                                  editable=editable)
  2418.         self.entNewJobOption.set_text ('')
  2419.         self.entNewJobOption.set_sensitive (editable)
  2420.         self.btnNewJobOption.set_sensitive (False)
  2421.  
  2422.         if printer.is_class:
  2423.             # remove InstallOptions tab
  2424.             tab_nr = self.ntbkPrinter.page_num(self.swPInstallOptions)
  2425.             if tab_nr != -1:
  2426.                 self.ntbkPrinter.remove_page(tab_nr)
  2427.             self.fillClassMembers(name, editable)
  2428.         else:
  2429.             # real Printer
  2430.             self.fillPrinterOptions(name, editablePPD)
  2431.  
  2432.         self.updateMarkerLevels()
  2433.         self.updateStateReasons()
  2434.         self.updatePrinterPropertiesTreeView()
  2435.  
  2436.         self.changed = set() # of options
  2437.         self.updatePrinterProperties ()
  2438.         self.setDataButtonState()
  2439.  
  2440.     def updatePrinterPropertiesTreeView (self):
  2441.         # Now update the tree view (which we use instead of the notebook tabs).
  2442.         store = gtk.ListStore (gobject.TYPE_STRING, gobject.TYPE_INT)
  2443.         self.ntbkPrinter.set_show_tabs (False)
  2444.         for n in range (self.ntbkPrinter.get_n_pages ()):
  2445.             page = self.ntbkPrinter.get_nth_page (n)
  2446.             label = self.ntbkPrinter.get_tab_label (page)
  2447.             iter = store.append (None)
  2448.             store.set_value (iter, 0, label.get_text ())
  2449.             store.set_value (iter, 1, n)
  2450.         sel = self.tvPrinterProperties.get_selection ()
  2451.         self.tvPrinterProperties.set_model (store)
  2452.  
  2453.     def updateMarkerLevels (self):
  2454.         printer = self.printer
  2455.  
  2456.         # Marker levels
  2457.         for widget in self.vboxMarkerLevels.get_children ():
  2458.             self.vboxMarkerLevels.remove (widget)
  2459.  
  2460.         marker_info = dict()
  2461.         for attr in ['marker-colors', 'marker-names', 'marker-types',
  2462.                      'marker-levels']:
  2463.             marker_info[attr] = printer.other_attributes.get (attr, [])
  2464.  
  2465.         markers = map (lambda color, name, type, level:
  2466.                            (color, name, type, level),
  2467.                        marker_info['marker-colors'],
  2468.                        marker_info['marker-names'],
  2469.                        marker_info['marker-types'],
  2470.                        marker_info['marker-levels'])
  2471.         debugprint (markers)
  2472.  
  2473.         can_refresh = (self.printer.type & cups.CUPS_PRINTER_COMMANDS) != 0
  2474.         self.btnRefreshMarkerLevels.set_sensitive (can_refresh)
  2475.         if len (markers) == 0:
  2476.             label = gtk.Label(_("Marker levels are not reported "
  2477.                                 "for this printer."))
  2478.             label.set_line_wrap (True)
  2479.             label.set_alignment (0.0, 0.0)
  2480.             self.vboxMarkerLevels.pack_start (label, False, False, 0)
  2481.         else:
  2482.             num_markers = 0
  2483.             cols = len (markers)
  2484.             rows = 1 + (cols - 1) / 4
  2485.             if cols > 4:
  2486.                 cols = 4
  2487.             table = gtk.Table (rows=rows,
  2488.                                columns=cols,
  2489.                                homogeneous=True)
  2490.             table.set_col_spacings (6)
  2491.             table.set_row_spacings (12)
  2492.             self.vboxMarkerLevels.pack_start (table)
  2493.             for color, name, marker_type, level in markers:
  2494.                 if name == None:
  2495.                     name = ''
  2496.  
  2497.                 row = num_markers / 4
  2498.                 col = num_markers % 4
  2499.  
  2500.                 vbox = gtk.VBox (spacing=6)
  2501.                 subhbox = gtk.HBox ()
  2502.                 inklevel = gtkinklevel.GtkInkLevel (color, level)
  2503.                 subhbox.pack_start (inklevel, True, False, 0)
  2504.                 vbox.pack_start (subhbox, False, False, 0)
  2505.                 label = gtk.Label (name)
  2506.                 label.set_line_wrap (True)
  2507.                 vbox.pack_start (label, False, False, 0)
  2508.                 table.attach (vbox, col, col + 1, row, row + 1)
  2509.                 num_markers += 1
  2510.  
  2511.         self.vboxMarkerLevels.show_all ()
  2512.  
  2513.     def on_btnRefreshMarkerLevels_clicked (self, button):
  2514.         self.maintenance_command ("ReportLevels")
  2515.  
  2516.     def updateStateReasons (self):
  2517.         printer = self.printer
  2518.         reasons = printer.other_attributes.get ('printer-state-reasons', [])
  2519.         store = gtk.ListStore (int, str)
  2520.         any = False
  2521.         for reason in reasons:
  2522.             if reason == "none":
  2523.                 break
  2524.  
  2525.             any = True
  2526.             iter = store.append (None)
  2527.             r = statereason.StateReason (printer.name, reason)
  2528.             store.set_value (iter, 0, r.get_level ())
  2529.             (title, text) = r.get_description ()
  2530.             store.set_value (iter, 1, text)
  2531.  
  2532.         self.tvPrinterStateReasons.set_model (store)
  2533.         page = 0
  2534.         if any:
  2535.             page = 1
  2536.  
  2537.         self.ntbkPrinterStateReasons.set_current_page (page)
  2538.  
  2539.     def set_printer_state_reason_icon (self, column, cell, model, iter, *data):
  2540.         level = model.get_value (iter, 0)
  2541.         icon = statereason.StateReason.LEVEL_ICON[level]
  2542.         theme = gtk.icon_theme_get_default ()
  2543.         try:
  2544.             pixbuf = theme.load_icon (icon, 22, 0)
  2545.             cell.set_property ("pixbuf", pixbuf)
  2546.         except gobject.GError, exc:
  2547.             pass # Couldn't load icon
  2548.  
  2549.     def set_printer_state_reason_text (self, column, cell, model, iter, *data):
  2550.         cell.set_property ("text", model.get_value (iter, 1))
  2551.  
  2552.     def updatePrinterProperties(self):
  2553.         debugprint ("update printer properties")
  2554.         printer = self.printer
  2555.         self.lblPMakeModel.set_text(printer.make_and_model)
  2556.         state = self.printer_states.get (printer.state, _("Unknown"))
  2557.         reason = printer.other_attributes.get ('printer-state-message', '')
  2558.         if len (reason) > 0:
  2559.             state += ' - ' + reason
  2560.         self.lblPState.set_text(state)
  2561.         if len (self.changed) == 0:
  2562.             debugprint ("no changes yet: full printer properties update")
  2563.             # State
  2564.             self.chkPEnabled.set_active(printer.enabled)
  2565.             self.chkPAccepting.set_active(not printer.rejecting)
  2566.             self.chkPShared.set_active(printer.is_shared)
  2567.  
  2568.             # Job sheets
  2569.             self.fillComboBox(self.cmbPStartBanner,
  2570.                               printer.job_sheets_supported,
  2571.                               printer.job_sheet_start,
  2572.                               ppdippstr.job_sheets),
  2573.             self.fillComboBox(self.cmbPEndBanner, printer.job_sheets_supported,
  2574.                               printer.job_sheet_end,
  2575.                               ppdippstr.job_sheets)
  2576.  
  2577.             # Policies
  2578.             self.fillComboBox(self.cmbPErrorPolicy,
  2579.                               printer.error_policy_supported,
  2580.                               printer.error_policy,
  2581.                               ppdippstr.printer_error_policy)
  2582.             self.fillComboBox(self.cmbPOperationPolicy,
  2583.                               printer.op_policy_supported,
  2584.                               printer.op_policy,
  2585.                               ppdippstr.printer_op_policy)
  2586.  
  2587.             # Access control
  2588.             self.rbtnPAllow.set_active(printer.default_allow)
  2589.             self.rbtnPDeny.set_active(not printer.default_allow)
  2590.             self.setPUsers(printer.except_users)
  2591.  
  2592.             # Marker levels
  2593.             self.updateMarkerLevels ()
  2594.             self.updateStateReasons ()
  2595.  
  2596.             self.updatePrinterPropertiesTreeView
  2597.  
  2598.     def fillPrinterOptions(self, name, editable):
  2599.         # remove Class membership tab
  2600.         tab_nr = self.ntbkPrinter.page_num(self.algnClassMembers)
  2601.         if tab_nr != -1:
  2602.             self.ntbkPrinter.remove_page(tab_nr)
  2603.  
  2604.         # clean Installable Options Tab
  2605.         for widget in self.vbPInstallOptions.get_children():
  2606.             self.vbPInstallOptions.remove(widget)
  2607.  
  2608.         # clean Options Tab
  2609.         for widget in self.vbPOptions.get_children():
  2610.             self.vbPOptions.remove(widget)
  2611.  
  2612.         # insert Options Tab
  2613.         if self.ntbkPrinter.page_num(self.swPOptions) == -1:
  2614.             self.ntbkPrinter.insert_page(
  2615.                 self.swPOptions, self.lblPOptions, self.static_tabs)
  2616.  
  2617.         if not self.ppd:
  2618.             tab_nr = self.ntbkPrinter.page_num(self.swPInstallOptions)
  2619.             if tab_nr != -1:
  2620.                 self.ntbkPrinter.remove_page(tab_nr)
  2621.             tab_nr = self.ntbkPrinter.page_num(self.swPOptions)
  2622.             if tab_nr != -1:
  2623.                 self.ntbkPrinter.remove_page(tab_nr)
  2624.             return
  2625.         ppd = self.ppd
  2626.         ppd.markDefaults()
  2627.         self.ppd_local.markDefaults()
  2628.  
  2629.         hasInstallableOptions = False
  2630.  
  2631.         # build option tabs
  2632.         for group in self.ppd_local.optionGroups:
  2633.             if group.name == "InstallableOptions":
  2634.                 hasInstallableOptions = True
  2635.                 container = self.vbPInstallOptions
  2636.                 tab_nr = self.ntbkPrinter.page_num(self.swPInstallOptions)
  2637.                 if tab_nr == -1:
  2638.                     self.ntbkPrinter.insert_page(self.swPInstallOptions,
  2639.                                                  gtk.Label(group.text),
  2640.                                                  self.static_tabs)
  2641.                 tab_label = self.lblPInstallOptions
  2642.             else:
  2643.                 frame = gtk.Frame("<b>%s</b>" % ppdippstr.ppd.get (group.text))
  2644.                 frame.get_label_widget().set_use_markup(True)
  2645.                 frame.set_shadow_type (gtk.SHADOW_NONE)
  2646.                 self.vbPOptions.pack_start (frame, False, False, 0)
  2647.                 container = gtk.Alignment (0.5, 0.5, 1.0, 1.0)
  2648.                 # We want a left padding of 12, but there is a Table with
  2649.                 # spacing 6, and the left-most column of it (the conflict
  2650.                 # icon) is normally hidden, so just use 6 here.
  2651.                 container.set_padding (6, 12, 6, 0)
  2652.                 frame.add (container)
  2653.                 tab_label = self.lblPOptions
  2654.  
  2655.             table = gtk.Table(1, 3, False)
  2656.             table.set_col_spacings(6)
  2657.             table.set_row_spacings(6)
  2658.             container.add(table)
  2659.  
  2660.             rows = 0
  2661.  
  2662.             # InputSlot and ManualFeed need special handling.  With
  2663.             # libcups, if ManualFeed is True, InputSlot gets unset.
  2664.             # Likewise, if InputSlot is set, ManualFeed becomes False.
  2665.             # We handle it by toggling the sensitivity of InputSlot
  2666.             # based on ManualFeed.
  2667.             self.option_inputslot = self.option_manualfeed = None
  2668.  
  2669.             for nr, option in enumerate(group.options):
  2670.                 if option.keyword == "PageRegion":
  2671.                     continue
  2672.                 rows += 1
  2673.                 table.resize (rows, 3)
  2674.                 o = OptionWidget(option, ppd, self, tab_label=tab_label)
  2675.                 table.attach(o.conflictIcon, 0, 1, nr, nr+1, 0, 0, 0, 0)
  2676.  
  2677.                 hbox = gtk.HBox()
  2678.                 if o.label:
  2679.                     a = gtk.Alignment (0.5, 0.5, 1.0, 1.0)
  2680.                     a.set_padding (0, 0, 0, 6)
  2681.                     a.add (o.label)
  2682.                     table.attach(a, 1, 2, nr, nr+1, gtk.FILL, 0, 0, 0)
  2683.                     table.attach(hbox, 2, 3, nr, nr+1, gtk.FILL, 0, 0, 0)
  2684.                 else:
  2685.                     table.attach(hbox, 1, 3, nr, nr+1, gtk.FILL, 0, 0, 0)
  2686.                 hbox.pack_start(o.selector, False)
  2687.                 self.options[option.keyword] = o
  2688.                 o.selector.set_sensitive(editable)
  2689.                 if option.keyword == "InputSlot":
  2690.                     self.option_inputslot = o
  2691.                 elif option.keyword == "ManualFeed":
  2692.                     self.option_manualfeed = o
  2693.  
  2694.         # remove Installable Options tab if not needed
  2695.         if not hasInstallableOptions:
  2696.             tab_nr = self.ntbkPrinter.page_num(self.swPInstallOptions)
  2697.             if tab_nr != -1:
  2698.                 self.ntbkPrinter.remove_page(tab_nr)
  2699.  
  2700.         # check for conflicts
  2701.         for option in self.options.itervalues():
  2702.             conflicts = option.checkConflicts()
  2703.             if conflicts:
  2704.                 self.conflicts.add(option)
  2705.  
  2706.         self.swPInstallOptions.show_all()
  2707.         self.swPOptions.show_all()
  2708.  
  2709.     # Class members
  2710.  
  2711.     def fillClassMembers(self, name, editable):
  2712.         printer = self.printers[name]
  2713.  
  2714.         self.btnClassAddMember.set_sensitive(editable)
  2715.         self.btnClassDelMember.set_sensitive(editable)
  2716.  
  2717.         # remove Options tab
  2718.         tab_nr = self.ntbkPrinter.page_num(self.swPOptions)
  2719.         if tab_nr != -1:
  2720.             self.ntbkPrinter.remove_page(tab_nr)
  2721.  
  2722.         # insert Member Tab
  2723.         if self.ntbkPrinter.page_num(self.algnClassMembers) == -1:
  2724.             self.ntbkPrinter.insert_page(
  2725.                 self.algnClassMembers, self.lblClassMembers,
  2726.                 self.static_tabs)
  2727.  
  2728.         model_members = self.tvClassMembers.get_model()
  2729.         model_not_members = self.tvClassNotMembers.get_model()
  2730.         model_members.clear()
  2731.         model_not_members.clear()
  2732.  
  2733.         names = self.printers.keys()
  2734.         names.sort()
  2735.         for name in names:
  2736.             p = self.printers[name]
  2737.             if p is not printer:
  2738.                 if name in printer.class_members:
  2739.                     model_members.append((name, ))
  2740.                 else:
  2741.                     model_not_members.append((name, ))
  2742.  
  2743.     def on_btnClassAddMember_clicked(self, button):
  2744.         moveClassMembers(self.tvClassNotMembers,
  2745.                          self.tvClassMembers)
  2746.         if getCurrentClassMembers(self.tvClassMembers) != self.printer.class_members:
  2747.             self.changed.add(self.tvClassMembers)
  2748.         else:
  2749.             self.changed.discard(self.tvClassMembers)
  2750.         self.setDataButtonState()
  2751.  
  2752.     def on_btnClassDelMember_clicked(self, button):
  2753.         moveClassMembers(self.tvClassMembers,
  2754.                          self.tvClassNotMembers)
  2755.         if getCurrentClassMembers(self.tvClassMembers) != self.printer.class_members:
  2756.             self.changed.add(self.tvClassMembers)
  2757.         else:
  2758.             self.changed.discard(self.tvClassMembers)
  2759.         self.setDataButtonState()
  2760.  
  2761.     # Quit
  2762.  
  2763.     def on_quit_activate(self, widget, event=None):
  2764.         self.monitor.cleanup ()
  2765.         while len (self.jobviewers) > 0:
  2766.             self.jobviewers[0].cleanup () # this will call on_jobviewer_exit
  2767.         gtk.main_quit()
  2768.  
  2769.     # Rename
  2770.     def is_rename_possible (self, name):
  2771.         jobs = self.printers[name].jobsQueued ()
  2772.         if len (jobs) > 0:
  2773.             show_error_dialog (_("Cannot Rename"),
  2774.                                _("There are queued jobs."),
  2775.                                parent=self.PrintersWindow)
  2776.             return False
  2777.  
  2778.         return True
  2779.  
  2780.     def on_rename_activate(self, UNUSED):
  2781.         tuple = self.dests_iconview.get_cursor ()
  2782.         if tuple == None:
  2783.             return
  2784.  
  2785.         (path, cell) = tuple
  2786.         if type (cell) != gtk.CellRendererText:
  2787.             cells = self.dests_iconview.get_cells ()
  2788.             for cell in cells:
  2789.                 if type (cell) == gtk.CellRendererText:
  2790.                     break
  2791.             if type (cell) != gtk.CellRendererText:
  2792.                 return
  2793.  
  2794.         model = self.dests_iconview.get_model ()
  2795.         iter = model.get_iter (path)
  2796.         name = unicode (model.get_value (iter, 2))
  2797.         if not self.is_rename_possible (name):
  2798.             return
  2799.         cell.set_property ('editable', True)
  2800.         self.dests_iconview.set_cursor (path, cell, start_editing=True)
  2801.         ids = []
  2802.         ids.append (cell.connect ('edited', self.printer_name_edited))
  2803.         ids.append (cell.connect ('editing-canceled',
  2804.                                  self.printer_name_edit_cancel))
  2805.         self.rename_sigids = ids
  2806.  
  2807.     def printer_name_edited (self, cell, path, newname):
  2808.         model = self.dests_iconview.get_model ()
  2809.         iter = model.get_iter (path)
  2810.         name = unicode (model.get_value (iter, 2))
  2811.         debugprint ("edited: %s -> %s" % (name, newname))
  2812.         try:
  2813.             self.rename_printer (name, newname)
  2814.         finally:
  2815.             cell.stop_editing (canceled=False)
  2816.             cell.set_property ('editable', False)
  2817.             for id in self.rename_sigids:
  2818.                 cell.disconnect (id)
  2819.  
  2820.     def printer_name_edit_cancel (self, cell):
  2821.         debugprint ("editing-canceled")
  2822.         cell.stop_editing (canceled=True)
  2823.         cell.set_property ('editable', False)
  2824.         for id in self.rename_sigids:
  2825.             cell.disconnect (id)
  2826.  
  2827.     def rename_printer (self, old_name, new_name):
  2828.         if old_name == new_name:
  2829.             return
  2830.  
  2831.         try:
  2832.             self.fillPrinterTab (old_name)
  2833.         except RuntimeError:
  2834.             # Perhaps cupsGetPPD2 failed for a browsed printer
  2835.             pass
  2836.  
  2837.         if not self.is_rename_possible (old_name):
  2838.             return
  2839.  
  2840.         self.cups._begin_operation (_("renaming printer"))
  2841.         rejecting = self.printer.rejecting
  2842.         if not rejecting:
  2843.             try:
  2844.                 self.printer.setAccepting (False)
  2845.                 if not self.is_rename_possible (old_name):
  2846.                     self.printer.setAccepting (True)
  2847.                     self.cups._end_operation ()
  2848.                     return
  2849.             except cups.IPPError, (e, msg):
  2850.                 show_IPP_Error (e, msg, self.PrintersWindow)
  2851.                 self.cups._end_operation ()
  2852.                 return
  2853.  
  2854.         if self.copy_printer (new_name):
  2855.             # Failure.
  2856.             self.monitor.update ()
  2857.  
  2858.             # Restore original accepting/rejecting state.
  2859.             if not rejecting:
  2860.                 try:
  2861.                     self.printers[old_name].setAccepting (True)
  2862.                 except cups.HTTPError, (s,):
  2863.                     show_HTTP_Error (s, self.PrintersWindow)
  2864.                 except cups.IPPError, (e, msg):
  2865.                     show_IPP_Error (e, msg, self.PrintersWindow)
  2866.  
  2867.             self.cups._end_operation ()
  2868.             return
  2869.  
  2870.         # Restore rejecting state.
  2871.         if not rejecting:
  2872.             try:
  2873.                 self.printer.setAccepting (True)
  2874.             except cups.HTTPError, (s,):
  2875.                 show_HTTP_Error (s, self.PrintersWindow)
  2876.                 # Not fatal.
  2877.             except cups.IPPError, (e, msg):
  2878.                 show_IPP_Error (e, msg, self.PrintersWindow)
  2879.                 # Not fatal.
  2880.  
  2881.         # Fix up default printer.
  2882.         if self.default_printer == old_name:
  2883.             reload = False
  2884.             try:
  2885.                 reload = self.printer.setAsDefault ()
  2886.             except cups.HTTPError, (s,):
  2887.                 show_HTTP_Error (s, self.PrintersWindow)
  2888.                 # Not fatal.
  2889.             except CUPS.IPPError, (e, msg):
  2890.                 show_IPP_Error (e, msg, self.PrintersWindow)
  2891.                 # Not fatal.
  2892.  
  2893.             if reload:
  2894.                 self.reconnect ()
  2895.  
  2896.         # Finally, delete the old printer.
  2897.         try:
  2898.             self.cups.deletePrinter (old_name)
  2899.         except cups.HTTPError, (s,):
  2900.             show_HTTP_Error (s, self.PrintersWindow)
  2901.             # Not fatal
  2902.         except cups.IPPError, (e, msg):
  2903.             show_IPP_Error (e, msg, self.PrintersWindow)
  2904.             # Not fatal.
  2905.  
  2906.         self.cups._end_operation ()
  2907.  
  2908.         # ..and select the new printer.
  2909.         def select_new_printer (model, path, iter):
  2910.             name = unicode (model.get_value (iter, 2))
  2911.             print name, new_name
  2912.             if name == new_name:
  2913.                 self.dests_iconview.select_path (path)
  2914.         self.populateList ()
  2915.         model = self.dests_iconview.get_model ()
  2916.         model.foreach (select_new_printer)
  2917.  
  2918.     # Copy
  2919.  
  2920.     def copy_printer (self, new_name):
  2921.         self.printer.name = new_name
  2922.         self.printer.class_members = [] # for classes make sure all members
  2923.                                         # will get added
  2924.  
  2925.         self.cups._begin_operation (_("copying printer"))
  2926.         ret = self.save_printer(self.printer, saveall=True,
  2927.                                 parent=self.PrintersWindow)
  2928.         self.cups._end_operation ()
  2929.         return ret
  2930.  
  2931.     def on_copy_activate(self, UNUSED):
  2932.         iconview = self.dests_iconview
  2933.         paths = iconview.get_selected_items ()
  2934.         model = self.dests_iconview.get_model ()
  2935.         iter = model.get_iter (paths[0])
  2936.         name = unicode (model.get_value (iter, 2))
  2937.         self.entCopyName.set_text(name)
  2938.         self.NewPrinterName.set_transient_for (self.PrintersWindow)
  2939.         result = self.NewPrinterName.run()
  2940.         self.NewPrinterName.hide()
  2941.  
  2942.         if result == gtk.RESPONSE_CANCEL:
  2943.             return
  2944.  
  2945.         try:
  2946.             self.fillPrinterTab (name)
  2947.         except RuntimeError:
  2948.             # Perhaps cupsGetPPD2 failed for a browsed printer
  2949.             pass
  2950.  
  2951.         self.copy_printer (self.entCopyName.get_text ())
  2952.         self.monitor.update ()
  2953.  
  2954.     def on_entCopyName_changed(self, widget):
  2955.         # restrict
  2956.         text = unicode (widget.get_text())
  2957.         new_text = text
  2958.         new_text = new_text.replace("/", "")
  2959.         new_text = new_text.replace("#", "")
  2960.         new_text = new_text.replace(" ", "")
  2961.         if text!=new_text:
  2962.             widget.set_text(new_text)
  2963.         self.btnCopyOk.set_sensitive(
  2964.             self.checkNPName(new_text))
  2965.  
  2966.     # Delete
  2967.  
  2968.     def on_delete_activate(self, UNUSED):
  2969.         if isinstance (self.current_groups_pane_item, StaticGroupItem):
  2970.             paths = self.dests_iconview.get_selected_items ()
  2971.             model = self.dests_iconview.get_model ()
  2972.             selected_names = []
  2973.             for path in paths:
  2974.                 selected_names.append (model[path][2])
  2975.             self.current_groups_pane_item.remove_queues (selected_names)
  2976.             self.populateList ()
  2977.         else:
  2978.             self.delete_selected_printer_queues ()
  2979.  
  2980.     def delete_selected_printer_queues (self):
  2981.         paths = self.dests_iconview.get_selected_items ()
  2982.         model = self.dests_iconview.get_model ()
  2983.         n = len (paths)
  2984.         if n == 1:
  2985.             iter = model.get_iter (paths[0])
  2986.             object = model.get_value (iter, 0)
  2987.             name = model.get_value (iter, 2)
  2988.             if object.is_class:
  2989.                 message_format = _("Really delete class '%s'?") % name
  2990.             else:
  2991.                 message_format = _("Really delete printer '%s'?") % name
  2992.         else:
  2993.             message_format = _("Really delete selected destinations?")
  2994.  
  2995.         dialog = gtk.MessageDialog(self.PrintersWindow,
  2996.                                    gtk.DIALOG_DESTROY_WITH_PARENT |
  2997.                                    gtk.DIALOG_MODAL,
  2998.                                    gtk.MESSAGE_WARNING,
  2999.                                    gtk.BUTTONS_NONE,
  3000.                                    message_format)
  3001.         dialog.add_buttons (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
  3002.                             gtk.STOCK_DELETE, gtk.RESPONSE_ACCEPT)
  3003.         dialog.set_default_response (gtk.RESPONSE_REJECT)
  3004.         result = dialog.run()
  3005.         dialog.destroy()
  3006.  
  3007.         if result != gtk.RESPONSE_ACCEPT:
  3008.             return
  3009.  
  3010.         try:
  3011.             for i in range (n):
  3012.                 iter = model.get_iter (paths[i])
  3013.                 name = model.get_value (iter, 2)
  3014.                 self.cups._begin_operation (_("deleting printer %s") % name)
  3015.                 name = unicode (name)
  3016.                 self.cups.deletePrinter (name)
  3017.                 self.cups._end_operation ()
  3018.         except cups.IPPError, (e, msg):
  3019.             self.cups._end_operation ()
  3020.             show_IPP_Error(e, msg, self.PrintersWindow)
  3021.  
  3022.         self.changed = set()
  3023.         self.monitor.update ()
  3024.  
  3025.     # Enable/disable
  3026.     def on_enabled_activate(self, toggle_action):
  3027.         if self.updating_widgets:
  3028.             return
  3029.         enable = toggle_action.get_active ()
  3030.         iconview = self.dests_iconview
  3031.         paths = iconview.get_selected_items ()
  3032.         model = iconview.get_model ()
  3033.         for i in range (len (paths)):
  3034.             iter = model.get_iter (paths[i])
  3035.             printer = model.get_value (iter, 0)
  3036.             name = unicode (model.get_value (iter, 2), 'utf-8')
  3037.             self.cups._begin_operation (_("modifying printer %s") % name)
  3038.             try:
  3039.                 printer.setEnabled (enable)
  3040.             except cups.IPPError, (e, m):
  3041.                 errordialogs.show_IPP_Error (e, m, self.PrintersWindow)
  3042.                 # Give up on this operation.
  3043.                 self.cups._end_operation ()
  3044.                 break
  3045.  
  3046.             self.cups._end_operation ()
  3047.  
  3048.         self.monitor.update ()
  3049.  
  3050.     # Shared
  3051.     def on_shared_activate(self, menuitem):
  3052.         if self.updating_widgets:
  3053.             return
  3054.         share = menuitem.get_active ()
  3055.         iconview = self.dests_iconview
  3056.         paths = iconview.get_selected_items ()
  3057.         model = iconview.get_model ()
  3058.         success = False
  3059.         for i in range (len (paths)):
  3060.             iter = model.get_iter (paths[i])
  3061.             printer = model.get_value (iter, 0)
  3062.             self.cups._begin_operation (_("modifying printer %s") %
  3063.                                         printer.name)
  3064.             try:
  3065.                 printer.setShared (share)
  3066.                 success = True
  3067.             except cups.IPPError, (e, m):
  3068.                 show_IPP_Error(e, m, self.PrintersWindow)
  3069.                 self.cups._end_operation ()
  3070.                 # Give up on this operation.
  3071.                 break
  3072.  
  3073.             self.cups._end_operation ()
  3074.  
  3075.         if success and share:
  3076.             self.advise_publish ()
  3077.  
  3078.         # For some reason CUPS doesn't give us a notification about
  3079.         # printers changing 'shared' state, so refresh instead of
  3080.         # update.  We have to defer this to prevent signal problems.
  3081.         def deferred_refresh ():
  3082.             self.populateList ()
  3083.             return False
  3084.         gobject.idle_add (deferred_refresh)
  3085.  
  3086.     def advise_publish(self):
  3087.         if not self.server_is_publishing:
  3088.             show_info_dialog (_("Publish Shared Printers"),
  3089.                               _("Shared printers are not available "
  3090.                                 "to other people unless the "
  3091.                                 "'Publish shared printers' option is "
  3092.                                 "enabled in the server settings."),
  3093.                               parent=self.PrintersWindow)
  3094.  
  3095.     # Set As Default
  3096.     def on_set_as_default_activate(self, UNUSED):
  3097.         iconview = self.dests_iconview
  3098.         paths = iconview.get_selected_items ()
  3099.         model = iconview.get_model ()
  3100.         iter = model.get_iter (paths[0])
  3101.         name = unicode (model.get_value (iter, 2))
  3102.         self.set_system_or_user_default_printer (name)
  3103.  
  3104.     def on_edit_activate (self, UNUSED):
  3105.         paths = self.dests_iconview.get_selected_items ()
  3106.         self.dests_iconview_item_activated (self.dests_iconview, paths[0])
  3107.  
  3108.     def on_create_class_activate (self, UNUSED):
  3109.         paths = self.dests_iconview.get_selected_items ()
  3110.         class_members = []
  3111.         model = self.dests_iconview.get_model ()
  3112.         for path in paths:
  3113.             iter = model.get_iter (path)
  3114.             name = unicode (model.get_value (iter, 2), 'utf-8')
  3115.             class_members.append (name)
  3116.         self.newPrinterGUI.init ("class")
  3117.         out_model = self.newPrinterGUI.tvNCNotMembers.get_model ()
  3118.         in_model = self.newPrinterGUI.tvNCMembers.get_model ()
  3119.         iter = out_model.get_iter_first ()
  3120.         while iter != None:
  3121.             next = out_model.iter_next (iter)
  3122.             data = out_model.get (iter, 0)
  3123.             if data[0] in class_members:
  3124.                 in_model.append (data)
  3125.                 out_model.remove (iter)
  3126.             iter = next
  3127.  
  3128.     def on_view_print_queue_activate (self, UNUSED):
  3129.         paths = self.dests_iconview.get_selected_items ()
  3130.         if len (paths):
  3131.             specific_dests = []
  3132.             model = self.dests_iconview.get_model ()
  3133.             for path in paths:
  3134.                 iter = model.get_iter (path)
  3135.                 name = unicode (model.get_value (iter, 2), 'utf-8')
  3136.                 specific_dests.append (name)
  3137.             viewer = jobviewer.JobViewer (None, None, my_jobs=False,
  3138.                                           specific_dests=specific_dests,
  3139.                                           exit_handler=self.on_jobviewer_exit,
  3140.                                           parent=self.PrintersWindow)
  3141.         else:
  3142.             viewer = jobviewer.JobViewer (None, None, my_jobs=False,
  3143.                                           exit_handler=self.on_jobviewer_exit,
  3144.                                           parent=self.PrintersWindow)
  3145.  
  3146.         self.jobviewers.append (viewer)
  3147.  
  3148.     def on_jobviewer_exit (self, viewer):
  3149.         i = self.jobviewers.index (viewer)
  3150.         del self.jobviewers[i]
  3151.  
  3152.     def on_view_groups_activate (self, widget):
  3153.         if widget.get_active ():
  3154.             if not self.groups_pane_visible:
  3155.                 # Show it.
  3156.                 self.view_area_vbox.remove (self.view_area_scrolledwindow)
  3157.                 self.view_area_hpaned.add2 (self.view_area_scrolledwindow)
  3158.                 self.view_area_vbox.add (self.view_area_hpaned)
  3159.                 self.view_area_vbox.show_all ()
  3160.                 self.groups_pane_visible = True
  3161.         else:
  3162.             if self.groups_pane_visible:
  3163.                 # Hide it.
  3164.                 self.view_area_vbox.remove (self.view_area_hpaned)
  3165.                 self.view_area_hpaned.remove (self.view_area_scrolledwindow)
  3166.                 self.view_area_vbox.add (self.view_area_scrolledwindow)
  3167.                 self.view_area_vbox.show_all ()
  3168.                 self.groups_pane_visible = False
  3169.  
  3170.     def on_view_discovered_printers_activate (self, UNUSED):
  3171.         self.populateList ()
  3172.  
  3173.     def on_troubleshoot_activate(self, widget):
  3174.         if not self.__dict__.has_key ('troubleshooter'):
  3175.             self.troubleshooter = troubleshoot.run (self.on_troubleshoot_quit)
  3176.  
  3177.     def on_troubleshoot_quit(self, troubleshooter):
  3178.         del self.troubleshooter
  3179.  
  3180.     def on_save_as_group_activate (self, UNUSED):
  3181.         model = self.dests_iconview.get_model ()
  3182.         printer_queues = []
  3183.         for object in model:
  3184.             printer_queues.append (object[2])
  3185.         self.groups_pane.create_new_group (printer_queues,
  3186.                                            self.current_filter_text)
  3187.  
  3188.     def on_save_as_search_group_activate (self, UNUSED):
  3189.         criterion = None
  3190.         if self.current_filter_mode == "filter-name":
  3191.             criterion = SearchCriterion (subject = SearchCriterion.SUBJECT_NAME,
  3192.                                          value   = self.current_filter_text)
  3193.         elif self.current_filter_mode == "filter-description":
  3194.             criterion = SearchCriterion (subject = SearchCriterion.SUBJECT_DESC,
  3195.                                          value   = self.current_filter_text)
  3196.         elif self.current_filter_mode == "filter-location":
  3197.             criterion = SearchCriterion (subject = SearchCriterion.SUBJECT_LOCATION,
  3198.                                          value   = self.current_filter_text)
  3199.         elif self.current_filter_mode == "filter-manufacturer":
  3200.             criterion = SearchCriterion (subject = SearchCriterion.SUBJECT_MANUF,
  3201.                                          value   = self.current_filter_text)
  3202.         else:
  3203.             nonfatalException ()
  3204.             return
  3205.  
  3206.         self.groups_pane.create_new_search_group (criterion,
  3207.                                                   self.current_filter_text)
  3208.  
  3209.     # About dialog
  3210.     def on_about_activate(self, widget):
  3211.         self.AboutDialog.set_transient_for (self.PrintersWindow)
  3212.         self.AboutDialog.run()
  3213.         self.AboutDialog.hide()
  3214.  
  3215.     ##########################################################################
  3216.     ### Server settings
  3217.     ##########################################################################
  3218.  
  3219.     def fillServerTab(self):
  3220.         self.changed = set()
  3221.         self.cups._begin_operation (_("fetching server settings"))
  3222.         try:
  3223.             self.server_settings = self.cups.adminGetServerSettings()
  3224.         except cups.IPPError, (e, m):
  3225.             show_IPP_Error(e, m, self.PrintersWindow)
  3226.             self.cups._end_operation ()
  3227.             raise
  3228.  
  3229.         self.cups._end_operation ()
  3230.  
  3231.         for widget, setting in [
  3232.             (self.chkServerBrowse, cups.CUPS_SERVER_REMOTE_PRINTERS),
  3233.             (self.chkServerShare, cups.CUPS_SERVER_SHARE_PRINTERS),
  3234.             (self.chkServerShareAny, try_CUPS_SERVER_REMOTE_ANY),
  3235.             (self.chkServerRemoteAdmin, cups.CUPS_SERVER_REMOTE_ADMIN),
  3236.             (self.chkServerAllowCancelAll, cups.CUPS_SERVER_USER_CANCEL_ANY),
  3237.             (self.chkServerLogDebug, cups.CUPS_SERVER_DEBUG_LOGGING),]:
  3238.             widget.set_data("setting", setting)
  3239.             if self.server_settings.has_key(setting):
  3240.                 widget.set_active(int(self.server_settings[setting]))
  3241.                 widget.set_sensitive(True)
  3242.             else:
  3243.                 widget.set_active(False)
  3244.                 widget.set_sensitive(False)
  3245.         self.setDataButtonState()
  3246.  
  3247.         try:
  3248.             flag = cups.CUPS_SERVER_SHARE_PRINTERS
  3249.             publishing = int (self.server_settings[flag])
  3250.             self.server_is_publishing = publishing
  3251.         except AttributeError:
  3252.             pass
  3253.  
  3254.         # Set sensitivity of 'Allow printing from the Internet'.
  3255.         self.on_server_changed (self.chkServerShare) # (any will do here)
  3256.  
  3257.     def on_server_changed(self, widget):
  3258.         setting = widget.get_data("setting")
  3259.         if self.server_settings.has_key (setting):
  3260.             if str(int(widget.get_active())) == self.server_settings[setting]:
  3261.                 self.changed.discard(widget)
  3262.             else:
  3263.                 self.changed.add(widget)
  3264.  
  3265.         sharing = self.chkServerShare.get_active ()
  3266.         self.chkServerShareAny.set_sensitive (
  3267.             sharing and self.server_settings.has_key(try_CUPS_SERVER_REMOTE_ANY))
  3268.  
  3269.         self.setDataButtonState()
  3270.  
  3271.     def save_serversettings(self):
  3272.         setting_dict = self.server_settings.copy()
  3273.         for widget, setting in [
  3274.             (self.chkServerBrowse, cups.CUPS_SERVER_REMOTE_PRINTERS),
  3275.             (self.chkServerShare, cups.CUPS_SERVER_SHARE_PRINTERS),
  3276.             (self.chkServerShareAny, try_CUPS_SERVER_REMOTE_ANY),
  3277.             (self.chkServerRemoteAdmin, cups.CUPS_SERVER_REMOTE_ADMIN),
  3278.             (self.chkServerAllowCancelAll, cups.CUPS_SERVER_USER_CANCEL_ANY),
  3279.             (self.chkServerLogDebug, cups.CUPS_SERVER_DEBUG_LOGGING),]:
  3280.             if not self.server_settings.has_key(setting): continue
  3281.             setting_dict[setting] = str(int(widget.get_active()))
  3282.         self.cups._begin_operation (_("modifying server settings"))
  3283.         try:
  3284.             self.cups.adminSetServerSettings(setting_dict)
  3285.         except cups.IPPError, (e, m):
  3286.             show_IPP_Error(e, m, self.ServerSettingsDialog)
  3287.             self.cups._end_operation ()
  3288.             return True
  3289.         except RuntimeError, s:
  3290.             show_IPP_Error(None, s, self.ServerSettingsDialog)
  3291.             self.cups._end_operation ()
  3292.             return True
  3293.         self.cups._end_operation ()
  3294.         self.changed = set()
  3295.         self.setDataButtonState()
  3296.  
  3297.         old_setting = self.server_settings.get (cups.CUPS_SERVER_SHARE_PRINTERS,
  3298.                                                 '0')
  3299.         new_setting = setting_dict.get (cups.CUPS_SERVER_SHARE_PRINTERS, '0')
  3300.         if (0 and old_setting == '0' and new_setting != '0'):
  3301.             # We have just enabled print queue sharing.
  3302.             # Ideally, this is the time we would check the firewall
  3303.             # settings on this machine and request that the IPP TCP port
  3304.             # be unblocked.  Unfortunately, this is not yet possible
  3305.             # (bug #440469).  However, we can display a dialog to suggest
  3306.             # that now might be a good time to review the firewall settings.
  3307.             show_info_dialog (_("Review Firewall"),
  3308.                               _("You may need to adjust the firewall "
  3309.                                 "to allow network printing to this "
  3310.                                 "computer.") + '\n\n' +
  3311.                               TEXT_start_firewall_tool,
  3312.                               parent=self.ServerSettingsDialog)
  3313.  
  3314.         time.sleep(1) # give the server a chance to process our request
  3315.  
  3316.         # Now reconnect, in case the server needed to reload.
  3317.         self.reconnect ()
  3318.  
  3319.         # Refresh the server settings in case they have changed in the
  3320.         # mean time.
  3321.         try:
  3322.             self.fillServerTab()
  3323.         except:
  3324.             nonfatalException()
  3325.  
  3326.     ### The "Problems?" clickable label
  3327.     def on_problems_button_clicked (self, *args):
  3328.         if not self.__dict__.has_key ('troubleshooter'):
  3329.             self.troubleshooter = troubleshoot.run (self.on_troubleshoot_quit,
  3330.                                                     parent=self.ServerSettingsDialog)
  3331.  
  3332.     # ====================================================================
  3333.     # == New Printer Dialog ==============================================
  3334.     # ====================================================================
  3335.  
  3336.     # new printer
  3337.     def on_new_printer_activate(self, widget):
  3338.         self.busy (self.PrintersWindow)
  3339.         self.newPrinterGUI.init("printer")
  3340.         self.ready (self.PrintersWindow)
  3341.  
  3342.     # new printer, auto-detected, but now driver found
  3343.     def on_autodetected_printer_without_driver(self, widget):
  3344.         self.busy (self.PrintersWindow)
  3345.         self.newPrinterGUI.init("printer_with_uri")
  3346.         self.ready (self.PrintersWindow)
  3347.  
  3348.     # new class
  3349.     def on_new_class_activate(self, widget):
  3350.         self.newPrinterGUI.init("class")
  3351.  
  3352.     # change device
  3353.     def on_btnSelectDevice_clicked(self, button):
  3354.         self.busy (self.PrintersWindow)
  3355.         self.newPrinterGUI.init("device")
  3356.         self.ready (self.PrintersWindow)
  3357.  
  3358.     # change PPD
  3359.     def on_btnChangePPD_clicked(self, button):
  3360.         self.busy (self.PrintersWindow)
  3361.         self.newPrinterGUI.init("ppd")
  3362.         self.ready (self.PrintersWindow)
  3363.  
  3364.     def checkNPName(self, name):
  3365.         if not name: return False
  3366.         name = unicode (name.lower())
  3367.         for printer in self.printers.values():
  3368.             if not printer.discovered and printer.name.lower()==name:
  3369.                 return False
  3370.         return True
  3371.  
  3372.     def makeNameUnique(self, name):
  3373.         """Make a suggested queue name valid and unique."""
  3374.         name = name.replace (" ", "-")
  3375.         name = name.replace ("/", "-")
  3376.         name = name.replace ("#", "-")
  3377.         if not self.checkNPName (name):
  3378.             suffix=2
  3379.             while not self.checkNPName (name + str (suffix)):
  3380.                 suffix += 1
  3381.                 if suffix == 100:
  3382.                     break
  3383.             name += str (suffix)
  3384.         return name
  3385.  
  3386.     ## Watcher interface helpers
  3387.     def printer_added_or_removed (self):
  3388.         # Just fetch the list of printers again.  This is too simplistic.
  3389.         gtk.gdk.threads_enter ()
  3390.         self.populateList (prompt_allowed=False)
  3391.         gtk.gdk.threads_leave ()
  3392.  
  3393.     ## Watcher interface
  3394.     def printer_added (self, mon, printer):
  3395.         monitor.Watcher.printer_added (self, mon, printer)
  3396.         self.printer_added_or_removed ()
  3397.  
  3398.     def printer_event (self, mon, printer, eventname, event):
  3399.         monitor.Watcher.printer_event (self, mon, printer, eventname, event)
  3400.         gtk.gdk.threads_enter ()
  3401.         if self.PrinterPropertiesDialog.get_property('visible'):
  3402.             self.printer.getAttributes ()
  3403.             self.updatePrinterProperties ()
  3404.  
  3405.         gtk.gdk.threads_leave ()
  3406.  
  3407.     def printer_removed (self, mon, printer):
  3408.         monitor.Watcher.printer_removed (self, mon, printer)
  3409.         self.printer_added_or_removed ()
  3410.  
  3411.     def state_reason_added (self, mon, reason):
  3412.         monitor.Watcher.state_reason_added (self, mon, reason)
  3413.         gtk.gdk.threads_enter ()
  3414.         if self.PrinterPropertiesDialog.get_property('visible'):
  3415.             self.printer.getAttributes ()
  3416.             self.updatePrinterProperties ()
  3417.  
  3418.         gtk.gdk.threads_leave ()
  3419.  
  3420.     def state_reason_removed (self, mon, reason):
  3421.         monitor.Watcher.state_reason_removed (self, mon, reason)
  3422.         gtk.gdk.threads_enter ()
  3423.         if self.PrinterPropertiesDialog.get_property('visible'):
  3424.             self.printer.getAttributes ()
  3425.             self.updatePrinterProperties ()
  3426.  
  3427.         gtk.gdk.threads_leave ()
  3428.  
  3429.     def cups_connection_error (self, mon):
  3430.         monitor.Watcher.cups_connection_error (self, mon)
  3431.         try:
  3432.             self.cups.getClasses ()
  3433.         except:
  3434.             self.cups = None
  3435.             gtk.gdk.threads_enter ()
  3436.             self.setConnected ()
  3437.             self.populateList (prompt_allowed=False)
  3438.             gtk.gdk.threads_leave ()
  3439.  
  3440. class NewPrinterGUI(GtkGUI):
  3441.  
  3442.     new_printer_device_tabs = {
  3443.         "parallel" : 0, # empty tab
  3444.         "usb" : 0,
  3445.         "hal" : 0,
  3446.         "beh" : 0,
  3447.         "hp" : 8,
  3448.         "hpfax" : 0,
  3449.         "socket": 2,
  3450.         "ipp" : 3,
  3451.         "http" : 3,
  3452.         "lpd" : 4,
  3453.         "scsi" : 5,
  3454.         "serial" : 6,
  3455.         "smb" : 7,
  3456.         "network": 9,
  3457.         }
  3458.  
  3459.     DOWNLOADABLE_ONLYPPD=True
  3460.     HP_PLUGIN_SUPPORT=True
  3461.  
  3462.     def __init__(self, mainapp):
  3463.         self.mainapp = mainapp
  3464.         self.tooltips = mainapp.tooltips
  3465.         self.language = mainapp.language
  3466.  
  3467.         self.options = {} # keyword -> Option object
  3468.         self.changed = set()
  3469.         self.conflicts = set()
  3470.         self.ppd = None
  3471.         self.remotecupsqueue = False
  3472.         self.exactdrivermatch = False
  3473.         self.installable_options = False
  3474.         self.jockey_installed_files = []
  3475.  
  3476.         # Synchronisation objects.
  3477.         self.jockey_lock = thread.allocate_lock()
  3478.         self.ppds_lock = thread.allocate_lock()
  3479.         self.ipp_lock = thread.allocate_lock()
  3480.         self.drivers_lock = thread.allocate_lock()
  3481.  
  3482.         self.getWidgets({"NewPrinterWindow":
  3483.                              ["NewPrinterWindow",
  3484.                               "ntbkNewPrinter",
  3485.                               "btnNPBack",
  3486.                               "btnNPForward",
  3487.                               "btnNPApply",
  3488.                               "entNPName",
  3489.                               "entNPDescription",
  3490.                               "entNPLocation",
  3491.                               "tvNPDevices",
  3492.                               "ntbkNPType",
  3493.                               "lblNPDeviceDescription",
  3494.                               "expNPDeviceURIs",
  3495.                               "tvNPDeviceURIs",
  3496.                               "cmbNPTSerialBaud",
  3497.                               "cmbNPTSerialParity",
  3498.                               "cmbNPTSerialBits",
  3499.                               "cmbNPTSerialFlow",
  3500.                               "cmbentNPTLpdHost",
  3501.                               "cmbentNPTLpdQueue",
  3502.                               "entNPTIPPHostname",
  3503.                               "btnIPPFindQueue",
  3504.                               "lblIPPURI",
  3505.                               "entNPTIPPQueuename",
  3506.                               "btnIPPVerify",
  3507.                               "entNPTDirectJetHostname",
  3508.                               "entNPTDirectJetPort",
  3509.                               "entSMBURI",
  3510.                               "btnSMBBrowse",
  3511.                               "tblSMBAuth",
  3512.                               "rbtnSMBAuthPrompt",
  3513.                               "rbtnSMBAuthSet",
  3514.                               "entSMBUsername",
  3515.                               "entSMBPassword",
  3516.                               "btnSMBVerify",
  3517.                               "entNPTHPHostname",
  3518.                               "btnHPFindQueue",
  3519.                               "entNPTNetworkHostname",
  3520.                               "btnNetworkFind",
  3521.                               "lblHPURI",
  3522.                               "entNPTDevice",
  3523.                               "tvNCMembers",
  3524.                               "tvNCNotMembers",
  3525.                               "ntbkPPDSource",
  3526.                               "rbtnNPPPD",
  3527.                               "tvNPMakes",
  3528.                               "rbtnNPFoomatic",
  3529.                               "filechooserPPD",
  3530.                               "rbtnNPDownloadableDriverSearch",
  3531.                               "entNPDownloadableDriverSearch",
  3532.                               "btnNPDownloadableDriverSearch",
  3533.                               "cmbNPDownloadableDriverFoundPrinters",
  3534.                               "tvNPModels",
  3535.                               "tvNPDrivers",
  3536.                               "rbtnChangePPDasIs",
  3537.                               "rbtnChangePPDKeepSettings",
  3538.                               "scrNPInstallableOptions",
  3539.                               "vbNPInstallOptions",
  3540.                               "tvNPDownloadableDrivers",
  3541.                               "ntbkNPDownloadableDriverProperties",
  3542.                               "lblNPDownloadableDriverSupplier",
  3543.                               "cbNPDownloadableDriverSupplierVendor",
  3544.                               "lblNPDownloadableDriverLicense",
  3545.                               "cbNPDownloadableDriverLicensePatents",
  3546.                               "cbNPDownloadableDriverLicenseFree",
  3547.                               "lblNPDownloadableDriverDescription",
  3548.                               "lblNPDownloadableDriverSupportContacts",
  3549.                               "hsDownloadableDriverPerfText",
  3550.                               "hsDownloadableDriverPerfLineArt",
  3551.                               "hsDownloadableDriverPerfGraphics",
  3552.                               "hsDownloadableDriverPerfPhoto",
  3553.                               "lblDownloadableDriverPerfTextUnknown",
  3554.                               "lblDownloadableDriverPerfLineArtUnknown",
  3555.                               "lblDownloadableDriverPerfGraphicsUnknown",
  3556.                               "lblDownloadableDriverPerfPhotoUnknown",
  3557.                               "frmNPDownloadableDriverLicenseTerms",
  3558.                               "tvNPDownloadableDriverLicense",
  3559.                               "rbtnNPDownloadLicenseYes",
  3560.                               "rbtnNPDownloadLicenseNo"],
  3561.                          "IPPBrowseDialog":
  3562.                              ["IPPBrowseDialog",
  3563.                               "tvIPPBrowser",
  3564.                               "btnIPPBrowseOk"],
  3565.                          "SMBBrowseDialog":
  3566.                              ["SMBBrowseDialog",
  3567.                               "tvSMBBrowser",
  3568.                               "btnSMBBrowseOk"],
  3569.                          "InstallDialog":
  3570.                              ["InstallDialog",
  3571.                               "lblInstall"]})
  3572.  
  3573.         # Since some dialogs are reused we can't let the delete-event's
  3574.         # default handler destroy them
  3575.         for dialog in [self.IPPBrowseDialog,
  3576.                        self.SMBBrowseDialog]:
  3577.             dialog.connect ("delete-event", on_delete_just_hide)
  3578.  
  3579.         # share with mainapp
  3580.         self.WaitWindow = mainapp.WaitWindow
  3581.         self.lblWait = mainapp.lblWait
  3582.         self.busy = mainapp.busy
  3583.         self.ready = mainapp.ready
  3584.  
  3585.         gtk_label_autowrap.set_autowrap(self.NewPrinterWindow)
  3586.  
  3587.         self.ntbkNewPrinter.set_show_tabs(False)
  3588.         self.ntbkPPDSource.set_show_tabs(False)
  3589.         self.ntbkNPType.set_show_tabs(False)
  3590.         self.ntbkNPDownloadableDriverProperties.set_show_tabs(False)
  3591.  
  3592.         # Set up OpenPrinting widgets.
  3593.         self.openprinting = cupshelpers.openprinting.OpenPrinting ()
  3594.         self.openprinting_query_handle = None
  3595.         combobox = self.cmbNPDownloadableDriverFoundPrinters
  3596.         cell = gtk.CellRendererText()
  3597.         combobox.pack_start (cell, True)
  3598.         combobox.add_attribute(cell, 'text', 0)
  3599.         if self.DOWNLOADABLE_ONLYPPD:
  3600.             for widget in [self.cbNPDownloadableDriverLicenseFree,
  3601.                            self.cbNPDownloadableDriverLicensePatents]:
  3602.                 widget.hide ()
  3603.  
  3604.         def protect_toggle (toggle_widget):
  3605.             active = toggle_widget.get_data ('protect_active')
  3606.             if active != None:
  3607.                 toggle_widget.set_active (active)
  3608.  
  3609.         for widget in [self.cbNPDownloadableDriverSupplierVendor,
  3610.                        self.cbNPDownloadableDriverLicenseFree,
  3611.                        self.cbNPDownloadableDriverLicensePatents]:
  3612.             widget.connect ('clicked', protect_toggle)
  3613.  
  3614.         for widget in [self.hsDownloadableDriverPerfText,
  3615.                        self.hsDownloadableDriverPerfLineArt,
  3616.                        self.hsDownloadableDriverPerfGraphics,
  3617.                        self.hsDownloadableDriverPerfPhoto]:
  3618.             widget.connect ('change-value',
  3619.                             lambda x, y, z: True)
  3620.  
  3621.         # Device list
  3622.         slct = self.tvNPDevices.get_selection ()
  3623.         slct.set_select_function (self.device_select_function)
  3624.         self.tvNPDevices.set_row_separator_func (self.device_row_separator_fn)
  3625.         self.tvNPDevices.connect ("row-activated", self.device_row_activated)
  3626.  
  3627.         # Devices expander
  3628.         self.expNPDeviceURIs.connect ("notify::expanded",
  3629.                                       self.on_expNPDeviceURIs_expanded)
  3630.  
  3631.         # SMB browser
  3632.         self.smb_store = gtk.TreeStore (gobject.TYPE_PYOBJECT)
  3633.         self.btnSMBBrowse.set_sensitive (PYSMB_AVAILABLE)
  3634.         if not PYSMB_AVAILABLE:
  3635.             self.btnSMBBrowse.set_tooltip_text (_("Browsing not available "
  3636.                                                   "(pysmbc not installed)"))
  3637.  
  3638.         self.tvSMBBrowser.set_model (self.smb_store)
  3639.  
  3640.         # SMB list columns
  3641.         col = gtk.TreeViewColumn (_("Share"))
  3642.         cell = gtk.CellRendererText ()
  3643.         col.pack_start (cell, False)
  3644.         col.set_cell_data_func (cell, self.smbbrowser_cell_share)
  3645.         self.tvSMBBrowser.append_column (col)
  3646.  
  3647.         col = gtk.TreeViewColumn (_("Comment"))
  3648.         cell = gtk.CellRendererText ()
  3649.         col.pack_start (cell, False)
  3650.         col.set_cell_data_func (cell, self.smbbrowser_cell_comment)
  3651.         self.tvSMBBrowser.append_column (col)
  3652.  
  3653.         slct = self.tvSMBBrowser.get_selection ()
  3654.         slct.set_select_function (self.smb_select_function)
  3655.  
  3656.         self.SMBBrowseDialog.set_transient_for(self.NewPrinterWindow)
  3657.  
  3658.         # IPP browser
  3659.         self.ipp_store = gtk.TreeStore (str, # queue
  3660.                                         str, # location
  3661.                                         gobject.TYPE_PYOBJECT) # dict
  3662.         self.tvIPPBrowser.set_model (self.ipp_store)
  3663.         self.ipp_store.set_sort_column_id (0, gtk.SORT_ASCENDING)
  3664.  
  3665.         # IPP list columns
  3666.         col = gtk.TreeViewColumn (_("Queue"), gtk.CellRendererText (),
  3667.                                   text=0)
  3668.         col.set_resizable (True)
  3669.         col.set_sort_column_id (0)
  3670.         self.tvIPPBrowser.append_column (col)
  3671.  
  3672.         col = gtk.TreeViewColumn (_("Location"), gtk.CellRendererText (),
  3673.                                   text=1)
  3674.         self.tvIPPBrowser.append_column (col)
  3675.         self.IPPBrowseDialog.set_transient_for(self.NewPrinterWindow)
  3676.  
  3677.         self.tvNPDriversTooltips = TreeViewTooltips(self.tvNPDrivers, self.NPDriversTooltips)
  3678.  
  3679.         ppd_filter = gtk.FileFilter()
  3680.         ppd_filter.set_name(_("PostScript Printer Description files (*.ppd, *.PPD, *.ppd.gz, *.PPD.gz, *.PPD.GZ)"))
  3681.         ppd_filter.add_pattern("*.ppd")
  3682.         ppd_filter.add_pattern("*.PPD")
  3683.         ppd_filter.add_pattern("*.ppd.gz")
  3684.         ppd_filter.add_pattern("*.PPD.gz")
  3685.         ppd_filter.add_pattern("*.PPD.GZ")
  3686.         self.filechooserPPD.add_filter(ppd_filter)
  3687.  
  3688.         ppd_filter = gtk.FileFilter()
  3689.         ppd_filter.set_name(_("All files (*)"))
  3690.         ppd_filter.add_pattern("*")
  3691.         self.filechooserPPD.add_filter(ppd_filter)
  3692.  
  3693.     def show_IPP_Error (self, exception, message):
  3694.         return show_IPP_Error (exception, message, parent=self.NewPrinterWindow)
  3695.  
  3696.     def option_changed(self, option):
  3697.         if option.is_changed():
  3698.             self.changed.add(option)
  3699.         else:
  3700.             self.changed.discard(option)
  3701.  
  3702.         if option.conflicts:
  3703.             self.conflicts.add(option)
  3704.         else:
  3705.             self.conflicts.discard(option)
  3706.         self.setDataButtonState()
  3707.  
  3708.         return
  3709.  
  3710.     def setDataButtonState(self):
  3711.         self.btnNPForward.set_sensitive(not bool(self.conflicts))
  3712.  
  3713.     def init(self, dialog_mode):
  3714.         self.dialog_mode = dialog_mode
  3715.         self.options = {} # keyword -> Option object
  3716.         self.changed = set()
  3717.         self.conflicts = set()
  3718.  
  3719.         combobox = self.cmbNPDownloadableDriverFoundPrinters
  3720.         combobox.set_model (gtk.ListStore (str, str))
  3721.         self.entNPDownloadableDriverSearch.set_text ('')
  3722.         button = self.btnNPDownloadableDriverSearch
  3723.         label = button.get_children ()[0].get_children ()[0].get_children ()[1]
  3724.         self.btnNPDownloadableDriverSearch_label = label
  3725.         label.set_text (_("Search"))
  3726.  
  3727.         if self.dialog_mode in ("printer", "printer_with_uri", "class"):
  3728.             if self.dialog_mode == "class":
  3729.                 name_proto = "class"
  3730.             else:
  3731.                 name_proto = "printer"
  3732.             self.entNPName.set_text (self.mainapp.makeNameUnique(name_proto))
  3733.             self.entNPName.grab_focus()
  3734.             for widget in [self.entNPLocation,
  3735.                            self.entNPDescription,
  3736.                            self.entSMBURI, self.entSMBUsername,
  3737.                            self.entSMBPassword]:
  3738.                 widget.set_text('')
  3739.  
  3740.         if self.dialog_mode == "printer_with_uri":
  3741.             device_dict = { }
  3742.             self.device = cupshelpers.Device (self.mainapp.device_uri,
  3743.                                               **device_dict)
  3744.  
  3745.         self.entNPTDirectJetPort.set_text('9100')
  3746.  
  3747.         if self.dialog_mode == "printer":
  3748.             self.NewPrinterWindow.set_title(_("New Printer"))
  3749.             # Start on devices page (1, not 0)
  3750.             self.ntbkNewPrinter.set_current_page(1)
  3751.             self.fillDeviceTab()
  3752.             self.rbtnNPFoomatic.set_active (True)
  3753.             self.on_rbtnNPFoomatic_toggled(self.rbtnNPFoomatic)
  3754.             # Start fetching information from CUPS in the background
  3755.             self.new_printer_PPDs_loaded = False
  3756.             self.queryPPDs ()
  3757.  
  3758.         elif self.dialog_mode == "class":
  3759.             self.NewPrinterWindow.set_title(_("New Class"))
  3760.             self.fillNewClassMembers()
  3761.             # Start on name page
  3762.             self.ntbkNewPrinter.set_current_page(0)
  3763.         elif self.dialog_mode == "device":
  3764.             self.NewPrinterWindow.set_title(_("Change Device URI"))
  3765.             self.ntbkNewPrinter.set_current_page(1)
  3766.             self.fillDeviceTab(self.mainapp.printer.device_uri)
  3767.         elif self.dialog_mode == "ppd" or \
  3768.             self.dialog_mode == "printer_with_uri":
  3769.             if self.dialog_mode == "ppd":
  3770.                 self.NewPrinterWindow.set_title(_("Change Driver"))
  3771.             else:
  3772.                 self.NewPrinterWindow.set_title(_("New Printer"))
  3773.             self.ntbkNewPrinter.set_current_page(2)
  3774.             self.rbtnNPFoomatic.set_active (True)
  3775.             self.on_rbtnNPFoomatic_toggled(self.rbtnNPFoomatic)
  3776.             self.rbtnChangePPDKeepSettings.set_active(True)
  3777.  
  3778.             self.auto_model = ""
  3779.             ppd = self.mainapp.ppd
  3780.             #self.mainapp.devid = "MFG:Samsung;MDL:ML-3560;DES:;CMD:GDI;"
  3781.             devid = self.mainapp.devid
  3782.             if self.dialog_mode == "ppd":
  3783.                 uri = self.mainapp.printer.device_uri
  3784.             else:
  3785.                 uri = self.device.uri
  3786.                 if not self.install_hplip_plugin(uri):
  3787.                     self.on_NPCancel(None)
  3788.                     return
  3789.             if devid != "":
  3790.                 try:
  3791.                     devid_dict = cupshelpers.parseDeviceID (devid)
  3792.                     self.loadPPDs ()
  3793.                     reloaded = 0
  3794.                     while reloaded < 2:
  3795.                         (status, ppdname) = self.ppds.\
  3796.                             getPPDNameFromDeviceID (devid_dict["MFG"],
  3797.                                                     devid_dict["MDL"],
  3798.                                                     devid_dict["DES"],
  3799.                                                     devid_dict["CMD"],
  3800.                                                     uri,
  3801.                                                     self.jockey_installed_files)
  3802.                         if (status != self.ppds.STATUS_SUCCESS and
  3803.                             reloaded == 0):
  3804.                             if self.fetchJockeyDriver ():
  3805.                                 try:
  3806.                                     self.dropPPDs ()
  3807.                                     self.loadPPDs ()
  3808.                                     reloaded = 1
  3809.                                 except:
  3810.                                     reloaded = 2
  3811.                             else:
  3812.                                 reloaded = 2
  3813.                         else:
  3814.                             reloaded = 2
  3815.                     if ppdname:
  3816.                         ppddict = self.ppds.getInfoFromPPDName (ppdname)
  3817.                         make_model = ppddict['ppd-make-and-model']
  3818.                         (self.auto_make, self.auto_model) = \
  3819.                             cupshelpers.ppds.ppdMakeModelSplit (make_model)
  3820.                         if (status == self.ppds.STATUS_SUCCESS and \
  3821.                             self.dialog_mode != "ppd"):
  3822.                             self.exactdrivermatch = True
  3823.                             self.fillMakeList()
  3824.                             self.ntbkNewPrinter.set_current_page(6)
  3825.                             self.nextNPTab(step = 0)
  3826.                         else:
  3827.                             self.exactdrivermatch = False
  3828.                     else:
  3829.                         self.auto_make = devid_dict["MFG"]
  3830.                         self.auto_model = devid_dict["MDL"]
  3831.                 except:
  3832.                     self.auto_make = devid_dict["MFG"]
  3833.                     self.auto_model = devid_dict["MDL"]
  3834.                 self.mainapp.devid = ""
  3835.             elif ppd:
  3836.                 attr = ppd.findAttr("Manufacturer")
  3837.                 if attr:
  3838.                     mfr = attr.value
  3839.                 else:
  3840.                     mfr = ""
  3841.                 makeandmodel = mfr
  3842.                 attr = ppd.findAttr("ModelName")
  3843.                 if not attr: attr = ppd.findAttr("ShortNickName")
  3844.                 if not attr: attr = ppd.findAttr("NickName")
  3845.                 if attr:
  3846.                     if attr.value.startswith(mfr):
  3847.                         makeandmodel = attr.value
  3848.                     else:
  3849.                         makeandmodel += ' ' + attr.value
  3850.                 else:
  3851.                     makeandmodel = ''
  3852.  
  3853.                 (self.auto_make,
  3854.                  self.auto_model) = \
  3855.                  cupshelpers.ppds.ppdMakeModelSplit (makeandmodel)
  3856.             else:
  3857.                 # Special CUPS names for a raw queue.
  3858.                 self.auto_make = 'Raw'
  3859.                 self.auto_model = 'Queue'
  3860.  
  3861.             try:
  3862.                 self.loadPPDs ()
  3863.             except cups.IPPError, (e, m):
  3864.                 show_IPP_Error (e, m, parent=self.mainapp.PrintersWindow)
  3865.                 return
  3866.             except:
  3867.                 return
  3868.  
  3869.             self.fillMakeList()
  3870.  
  3871.         self.setNPButtons()
  3872.         self.NewPrinterWindow.show()
  3873.  
  3874.     # Get a new driver with Jockey
  3875.  
  3876.     def queryJockeyDriver(self):
  3877.         debugprint ("queryJockeyDriver")
  3878.         if not self.jockey_lock.acquire(0):
  3879.             debugprint ("queryJockeyDriver: in progress")
  3880.             return
  3881.         debugprint ("Lock acquired for Jockey driver thread")
  3882.         # Start new thread
  3883.         devid = ""
  3884.         try:
  3885.             devid = self.device.id
  3886.         except:
  3887.             pass
  3888.         if devid == '':
  3889.             try:
  3890.                 devid = self.mainapp.devid
  3891.             except:
  3892.                 pass
  3893.         if devid == '':
  3894.             self.jockey_lock.release ()
  3895.             return
  3896.         thread.start_new_thread (self.getJockeyDriver_thread, (devid,))
  3897.         debugprint ("Jockey driver thread started")
  3898.  
  3899.     def getJockeyDriver_thread(self, id):
  3900.         debugprint ("Requesting driver from Jockey: %s" % id)
  3901.         self.jockey_driver_result = False
  3902.         self.jockey_installed_files = []
  3903.         try:
  3904.             bus = dbus.SessionBus()
  3905.             obj = bus.get_object("com.ubuntu.DeviceDriver", "/GUI")
  3906.             jockeyloader = \
  3907.                 dbus.Interface(obj, "com.ubuntu.DeviceDriver")
  3908.             (result, installedfiles) = \
  3909.                 jockeyloader.search_driver("printer_deviceid:%s" % id,
  3910.                                            timeout=999999)
  3911.             self.jockey_driver_result = result
  3912.             self.jockey_installed_files = installedfiles
  3913.             if result:
  3914.                 debugprint ("New driver downloaded and installed")
  3915.             else:
  3916.                 debugprint ("No new driver found or download rejected")
  3917.         except dbus.DBusException, e:
  3918.             self.jockey_driver_result = "D-Bus Error: %s" % e
  3919.             debugprint (self.jockey_driver_result)
  3920.         except Exception, e:
  3921.             nonfatalException()
  3922.             self.jockey_driver_result = e
  3923.             debugprint ("Non-D-Bus error on Jockey call: %s" % e)
  3924.  
  3925.         debugprint ("Releasing Jockey driver lock")
  3926.         self.jockey_lock.release ()
  3927.  
  3928.     def fetchJockeyDriver(self, parent=None):
  3929.         debugprint ("fetchJockeyDriver")
  3930.         self.queryJockeyDriver()
  3931.         time.sleep (0.1)
  3932.  
  3933.         # Keep the UI refreshed while we wait for the driver to load.
  3934.         waiting = False
  3935.         while (self.jockey_lock.locked()):
  3936.             if not waiting:
  3937.                 waiting = True
  3938.                 self.lblWait.set_markup ('<span weight="bold" size="larger">' +
  3939.                                          _('Searching') + '</span>\n\n' +
  3940.                                          _('Searching for downloadable drivers'))
  3941.                 if not parent:
  3942.                     parent = self.mainapp.PrintersWindow
  3943.                 self.WaitWindow.set_transient_for (parent)
  3944.                 self.WaitWindow.show ()
  3945.  
  3946.             while gtk.events_pending ():
  3947.                 gtk.main_iteration ()
  3948.  
  3949.             time.sleep (0.1)
  3950.  
  3951.         if waiting:
  3952.             self.WaitWindow.hide ()
  3953.  
  3954.         debugprint ("Driver download request finished")
  3955.         result = self.jockey_driver_result # atomic operation
  3956.         if isinstance (result, Exception):
  3957.             # Propagate exception.
  3958.             raise result
  3959.         return result
  3960.  
  3961.     # get PPDs
  3962.  
  3963.     def queryPPDs(self):
  3964.         debugprint ("queryPPDs")
  3965.         if not self.ppds_lock.acquire(0):
  3966.             debugprint ("queryPPDs: in progress")
  3967.             return
  3968.         debugprint ("Lock acquired for PPDs thread")
  3969.         # Start new thread
  3970.         thread.start_new_thread (self.getPPDs_thread, (self.language[0],))
  3971.         debugprint ("PPDs thread started")
  3972.  
  3973.     def getPPDs_thread(self, language):
  3974.         try:
  3975.             debugprint ("Connecting (PPDs)")
  3976.             cups.setUser (self.mainapp.connect_user)
  3977.             cups.setPasswordCB (lambda x: '')
  3978.             c = cups.Connection (host=self.mainapp.connect_server,
  3979.                                  encryption=self.mainapp.connect_encrypt)
  3980.             debugprint ("Fetching PPDs")
  3981.             ppds_dict = c.getPPDs()
  3982.             self.ppds_result = cupshelpers.ppds.PPDs(ppds_dict,
  3983.                                                      language=language)
  3984.             debugprint ("Closing connection (PPDs)")
  3985.             del c
  3986.         except cups.IPPError, (e, msg):
  3987.             self.ppds_result = cups.IPPError (e, msg)
  3988.         except Exception, e:
  3989.             nonfatalException()
  3990.             self.ppds_result = e
  3991.  
  3992.         debugprint ("Releasing PPDs lock")
  3993.         self.ppds_lock.release ()
  3994.  
  3995.     def fetchPPDs(self, parent=None):
  3996.         debugprint ("fetchPPDs")
  3997.         self.queryPPDs()
  3998.  
  3999.         # Keep the UI refreshed while we wait for the devices to load.
  4000.         waiting = False
  4001.         while (self.ppds_lock.locked()):
  4002.             if not waiting:
  4003.                 waiting = True
  4004.                 self.lblWait.set_markup ('<span weight="bold" size="larger">' +
  4005.                                          _('Searching') + '</span>\n\n' +
  4006.                                          _('Searching for drivers'))
  4007.                 if not parent:
  4008.                     parent = self.mainapp.PrintersWindow
  4009.                 self.WaitWindow.set_transient_for (parent)
  4010.                 self.WaitWindow.show_now ()
  4011.  
  4012.             while gtk.events_pending ():
  4013.                 gtk.main_iteration ()
  4014.  
  4015.             time.sleep (0.1)
  4016.  
  4017.         if waiting:
  4018.             self.WaitWindow.hide ()
  4019.  
  4020.         debugprint ("Got PPDs")
  4021.         result = self.ppds_result # atomic operation
  4022.         if isinstance (result, Exception):
  4023.             # Propagate exception.
  4024.             raise result
  4025.         return result
  4026.  
  4027.     def loadPPDs(self, parent=None):
  4028.         try:
  4029.             return self.ppds
  4030.         except:
  4031.             self.ppds = self.fetchPPDs (parent=parent)
  4032.             return self.ppds
  4033.  
  4034.     def dropPPDs(self):
  4035.         try:
  4036.             del self.ppds
  4037.         except:
  4038.             pass
  4039.  
  4040.     # Class members
  4041.  
  4042.     def fillNewClassMembers(self):
  4043.         model = self.tvNCMembers.get_model()
  4044.         model.clear()
  4045.         model = self.tvNCNotMembers.get_model()
  4046.         model.clear()
  4047.         for printer in self.mainapp.printers.itervalues():
  4048.             model.append((printer.name,))
  4049.  
  4050.     def on_btnNCAddMember_clicked(self, button):
  4051.         moveClassMembers(self.tvNCNotMembers, self.tvNCMembers)
  4052.         self.btnNPApply.set_sensitive(
  4053.             bool(getCurrentClassMembers(self.tvNCMembers)))
  4054.  
  4055.     def on_btnNCDelMember_clicked(self, button):
  4056.         moveClassMembers(self.tvNCMembers, self.tvNCNotMembers)
  4057.         self.btnNPApply.set_sensitive(
  4058.             bool(getCurrentClassMembers(self.tvNCMembers)))
  4059.  
  4060.     # Navigation buttons
  4061.  
  4062.     def on_NPCancel(self, widget, event=None):
  4063.         self.NewPrinterWindow.hide()
  4064.         if self.openprinting_query_handle != None:
  4065.             self.openprinting.cancelOperation (self.openprinting_query_handle)
  4066.             self.openprinting_query_handle = None
  4067.         return True
  4068.  
  4069.     def on_btnNPBack_clicked(self, widget):
  4070.         self.nextNPTab(-1)
  4071.  
  4072.     def on_btnNPForward_clicked(self, widget):
  4073.         self.nextNPTab()
  4074.  
  4075.     def nextNPTab(self, step=1):
  4076.         page_nr = self.ntbkNewPrinter.get_current_page()
  4077.  
  4078.         if self.dialog_mode == "class":
  4079.             order = [0, 4, 5]
  4080.         elif self.dialog_mode == "printer" or \
  4081.                 self.dialog_mode == "printer_with_uri":
  4082.             self.busy (self.NewPrinterWindow)
  4083.             if page_nr == 1: # Device (first page)
  4084.                 self.auto_make, self.auto_model = None, None
  4085.                 self.device.uri = self.getDeviceURI()
  4086.                 if not self.install_hplip_plugin(self.device.uri):
  4087.                     self.on_NPCancel(None)
  4088.                     return
  4089.                 uri = self.device.uri
  4090.                 if uri and uri.startswith ("smb://"):
  4091.                     uri = SMBURI (uri=uri[6:]).sanitize_uri ()
  4092.  
  4093.                 # Try to access the PPD, in this case our detected IPP
  4094.                 # printer is a queue on a remote CUPS server which is
  4095.                 # not automatically set up on our local CUPS server
  4096.                 # (for example DNS-SD broadcasted queue from Mac OS X)
  4097.                 self.remotecupsqueue = None
  4098.                 res = re.search ("ipp://(\S+(:\d+|))/printers/(\S+)", uri)
  4099.                 if res:
  4100.                     resg = res.groups()
  4101.                     try:
  4102.                         conn = httplib.HTTPConnection(resg[0])
  4103.                         conn.request("GET", "/printers/%s.ppd" % resg[2])
  4104.                         resp = conn.getresponse()
  4105.                         if resp.status == 200: self.remotecupsqueue = resg[2]
  4106.                     except:
  4107.                         pass
  4108.  
  4109.                     # We also want to fetch the printer-info and
  4110.                     # printer-location attributes, to pre-fill those
  4111.                     # fields for this new queue.
  4112.                     try:
  4113.                         if len (resg[1]) > 0:
  4114.                             port = int (resg[1])
  4115.                         else:
  4116.                             port = 631
  4117.  
  4118.                         encryption = cups.HTTP_ENCRYPT_IF_REQUESTED
  4119.                         c = cups.Connection (host=resg[0],
  4120.                                              port=port,
  4121.                                              encryption=encryption)
  4122.  
  4123.                         r = ['printer-info', 'printer-location']
  4124.                         attrs = c.getPrinterAttributes (uri=uri,
  4125.                                                         requested_attributes=r)
  4126.                         info = attrs.get ('printer-info', '')
  4127.                         location = attrs.get ('printer-location', '')
  4128.                         if len (info) > 0:
  4129.                             self.entNPDescription.set_text (info)
  4130.                         if len (location) > 0:
  4131.                             self.entNPLocation.set_text (location)
  4132.                     except:
  4133.                         nonfatalException ()
  4134.  
  4135.                 if (not self.remotecupsqueue and
  4136.                     not self.new_printer_PPDs_loaded):
  4137.                     try:
  4138.                         self.loadPPDs(self.NewPrinterWindow)
  4139.                     except cups.IPPError, (e, msg):
  4140.                         self.ready (self.NewPrinterWindow)
  4141.                         self.show_IPP_Error(e, msg)
  4142.                         return
  4143.                     except:
  4144.                         self.ready (self.NewPrinterWindow)
  4145.                         return
  4146.                     self.new_printer_PPDs_loaded = True
  4147.  
  4148.                 ppdname = None
  4149.                 try:
  4150.                     if self.remotecupsqueue:
  4151.                         # We have a remote CUPS queue, let the client queue
  4152.                         # stay raw so that the driver on the server gets used
  4153.                         ppdname = 'raw'
  4154.                         self.ppd = ppdname
  4155.                         name = self.remotecupsqueue
  4156.                         name = self.mainapp.makeNameUnique (name)
  4157.                         self.entNPName.set_text (name)
  4158.                     elif self.device.id:
  4159.                         id_dict = self.device.id_dict
  4160.                         reloaded = 0
  4161.                         while reloaded < 2:
  4162.                             (status, ppdname) = self.ppds.\
  4163.                                 getPPDNameFromDeviceID (id_dict["MFG"],
  4164.                                                         id_dict["MDL"],
  4165.                                                         id_dict["DES"],
  4166.                                                         id_dict["CMD"],
  4167.                                                         self.device.uri,
  4168.                                                         self.jockey_installed_files)
  4169.                             if (status != self.ppds.STATUS_SUCCESS and
  4170.                                 reloaded == 0):
  4171.                                 #if reloaded == 0:
  4172.                                 #self.device.id = "MFG:Samsung;MDL:ML-1610;DES:;CMD:GDI;"
  4173.                                 #id_dict = cupshelpers.parseDeviceID(self.device.id)
  4174.                                 if self.fetchJockeyDriver ():
  4175.                                     try:
  4176.                                         self.dropPPDs ()
  4177.                                         self.loadPPDs ()
  4178.                                         reloaded = 1
  4179.                                     except:
  4180.                                         reloaded = 2
  4181.                                 else:
  4182.                                     reloaded = 2
  4183.                             else:
  4184.                                 reloaded = 2
  4185.                     else:
  4186.                         (status, ppdname) = self.ppds.\
  4187.                             getPPDNameFromDeviceID ("Generic",
  4188.                                                     "Printer",
  4189.                                                     "Generic Printer",
  4190.                                                     [],
  4191.                                                     self.device.uri)
  4192.  
  4193.                     if ppdname and not self.remotecupsqueue:
  4194.                         ppddict = self.ppds.getInfoFromPPDName (ppdname)
  4195.                         make_model = ppddict['ppd-make-and-model']
  4196.                         (make, model) = \
  4197.                             cupshelpers.ppds.ppdMakeModelSplit (make_model)
  4198.                         self.auto_make = make
  4199.                         self.auto_model = model
  4200.                         if (status == self.ppds.STATUS_SUCCESS and \
  4201.                             self.dialog_mode != "ppd"):
  4202.                             self.exactdrivermatch = True
  4203.                         else:
  4204.                             self.exactdrivermatch = False
  4205.                 except:
  4206.                     nonfatalException ()
  4207.  
  4208.                 if not self.remotecupsqueue:
  4209.                     self.fillMakeList()
  4210.             elif page_nr == 3: # Model has been selected
  4211.                 if not self.device.id:
  4212.                     # Choose an appropriate name when no Device ID
  4213.                     # is available, based on the model the user has
  4214.                     # selected.
  4215.                     try:
  4216.                         model, iter = self.tvNPModels.get_selection ().\
  4217.                                       get_selected ()
  4218.                         name = model.get(iter, 0)[0]
  4219.                         name = self.mainapp.makeNameUnique (name)
  4220.                         self.entNPName.set_text (name)
  4221.                     except:
  4222.                         nonfatalException ()
  4223.  
  4224.             self.ready (self.NewPrinterWindow)
  4225.             if self.dialog_mode == "printer":
  4226.                 if self.remotecupsqueue:
  4227.                     order = [1, 0]
  4228.                 elif self.exactdrivermatch:
  4229.                     order = [1, 6, 0]
  4230.                 elif self.rbtnNPFoomatic.get_active():
  4231.                     order = [1, 2, 3, 6, 0]
  4232.                 elif self.rbtnNPPPD.get_active():
  4233.                     order = [1, 2, 6, 0]
  4234.                 else:
  4235.                     # Downloadable driver
  4236.                     order = [1, 2, 7, 6, 0]
  4237.             else:
  4238.                 if self.remotecupsqueue:
  4239.                     order = [0]
  4240.                 elif self.exactdrivermatch:
  4241.                     order = [6, 0]
  4242.                 elif self.rbtnNPFoomatic.get_active():
  4243.                     order = [2, 3, 6, 0]
  4244.                 elif self.rbtnNPPPD.get_active():
  4245.                     order = [2, 6, 0]
  4246.                 else:
  4247.                     # Downloadable driver
  4248.                     order = [2, 7, 6, 0]
  4249.         elif self.dialog_mode == "device":
  4250.             order = [1]
  4251.         elif self.dialog_mode == "ppd":
  4252.             if self.rbtnNPFoomatic.get_active():
  4253.                 order = [2, 3, 5, 6]
  4254.             elif self.rbtnNPPPD.get_active():
  4255.                 order = [2, 5, 6]
  4256.             else:
  4257.                 # Downloadable driver
  4258.                 order = [2, 7, 5, 6]
  4259.  
  4260.         next_page_nr = order[order.index(page_nr)+step]
  4261.  
  4262.         # fill Installable Options tab
  4263.         fetch_ppd = False
  4264.         try:
  4265.             if order.index (5) > -1:
  4266.                 # There is a copy settings page in this set
  4267.                 fetch_ppd = next_page_nr == 5 and step >= 0
  4268.         except ValueError:
  4269.             fetch_ppd = next_page_nr == 6 and step >= 0
  4270.  
  4271.         debugprint ("Will fetch ppd? %d" % fetch_ppd)
  4272.         if fetch_ppd:
  4273.             self.ppd = self.getNPPPD()
  4274.             self.installable_options = False
  4275.             if self.ppd == None:
  4276.                 return
  4277.  
  4278.             # Prepare Installable Options screen.
  4279.             if isinstance(self.ppd, cups.PPD):
  4280.                 self.fillNPInstallableOptions()
  4281.             else:
  4282.                 # Put a label there explaining why the page is empty.
  4283.                 ppd = self.ppd
  4284.                 self.ppd = None
  4285.                 self.fillNPInstallableOptions()
  4286.                 self.ppd = ppd
  4287.  
  4288.             if not self.installable_options:
  4289.                 if next_page_nr == 6:
  4290.                     # step over if empty
  4291.                     next_page_nr = order[order.index(next_page_nr)+1]
  4292.  
  4293.         # Step over empty Installable Options tab when moving backwards.
  4294.         if next_page_nr == 6 and not self.installable_options and step<0:
  4295.             next_page_nr = order[order.index(next_page_nr)-1]
  4296.  
  4297.         if step >= 0 and next_page_nr == 7: # About to show downloadable drivers
  4298.             if self.drivers_lock.locked ():
  4299.                 # Still searching for drivers.
  4300.                 self.lblWait.set_markup ('<span weight="bold" size="larger">' +
  4301.                                          _('Searching') + '</span>\n\n' +
  4302.                                          _('Searching for drivers'))
  4303.                 self.WaitWindow.set_transient_for (self.NewPrinterWindow)
  4304.                 self.WaitWindow.show ()
  4305.                 self.busy (self.NewPrinterWindow)
  4306.  
  4307.                 # Keep the UI refreshed while we wait for the drivers
  4308.                 # query to complete.
  4309.                 while self.drivers_lock.locked ():
  4310.                     while gtk.events_pending ():
  4311.                         gtk.main_iteration ()
  4312.                     time.sleep (0.1)
  4313.  
  4314.                 self.ready (self.NewPrinterWindow)
  4315.                 self.WaitWindow.hide ()
  4316.  
  4317.             self.fillDownloadableDrivers()
  4318.  
  4319.         if step >= 0 and next_page_nr == 0: # About to choose a name.
  4320.             # Suggest an appropriate name.
  4321.             name = None
  4322.             descr = None
  4323.  
  4324.             try:
  4325.                 if self.device.id and not self.device.type in ("socket", "lpd", "ipp", "bluetooth"):
  4326.                     name = self.device.id_dict["MDL"]
  4327.                     descr = "%s %s" % (self.device.id_dict["MFG"], self.device.id_dict["MDL"])
  4328.             except:
  4329.                 nonfatalException ()
  4330.  
  4331.             try:
  4332.                 if name == None and isinstance (self.ppd, cups.PPD):
  4333.                     mname = self.ppd.findAttr ("modelName").value
  4334.                     make, model = cupshelpers.ppds.ppdMakeModelSplit (mname)
  4335.                     name = "%s %s" % (make, model)
  4336.                     descr = "%s %s" % (make, model)
  4337.             except:
  4338.                 nonfatalException ()
  4339.  
  4340.             if name == None:
  4341.                 name = 'printer'
  4342.  
  4343.             name = self.mainapp.makeNameUnique (name)
  4344.             self.entNPName.set_text (name)
  4345.  
  4346.             if self.entNPDescription.get_text () == '' and descr:
  4347.                 self.entNPDescription.set_text (descr)
  4348.  
  4349.         self.ntbkNewPrinter.set_current_page(next_page_nr)
  4350.  
  4351.         self.setNPButtons()
  4352.  
  4353.     def setNPButtons(self):
  4354.         nr = self.ntbkNewPrinter.get_current_page()
  4355.  
  4356.         if self.dialog_mode == "device":
  4357.             self.btnNPBack.hide()
  4358.             self.btnNPForward.hide()
  4359.             self.btnNPApply.show()
  4360.             try:
  4361.                 uri = self.getDeviceURI ()
  4362.                 valid = validDeviceURI (uri)
  4363.             except AttributeError:
  4364.                 # No device selected yet.
  4365.                 valid = False
  4366.             self.btnNPApply.set_sensitive (valid)
  4367.             return
  4368.  
  4369.         if self.dialog_mode == "ppd":
  4370.             if nr == 5: # Apply
  4371.                 if not self.installable_options:
  4372.                     # There are no installable options, so this is the
  4373.                     # last page.
  4374.                     debugprint ("No installable options")
  4375.                     self.btnNPForward.hide ()
  4376.                     self.btnNPApply.show ()
  4377.                 else:
  4378.                     self.btnNPForward.show ()
  4379.                     self.btnNPApply.hide ()
  4380.                 return
  4381.             elif nr == 6:
  4382.                 self.btnNPForward.hide()
  4383.                 self.btnNPApply.show()
  4384.                 return
  4385.             else:
  4386.                 self.btnNPForward.show()
  4387.                 self.btnNPApply.hide()
  4388.             if nr == 2:
  4389.                 self.btnNPBack.hide()
  4390.                 self.btnNPForward.show()
  4391.                 downloadable_selected = False
  4392.                 if self.rbtnNPDownloadableDriverSearch.get_active ():
  4393.                     combobox = self.cmbNPDownloadableDriverFoundPrinters
  4394.                     iter = combobox.get_active_iter ()
  4395.                     if iter and combobox.get_model ().get_value (iter, 1):
  4396.                         downloadable_selected = True
  4397.  
  4398.                 self.btnNPForward.set_sensitive(bool(
  4399.                         self.rbtnNPFoomatic.get_active() or
  4400.                         self.filechooserPPD.get_filename() or
  4401.                         downloadable_selected))
  4402.                 return
  4403.             else:
  4404.                 self.btnNPBack.show()
  4405.  
  4406.         # class/printer
  4407.  
  4408.         if nr == 1: # Device
  4409.             valid = False
  4410.             try:
  4411.                 uri = self.getDeviceURI ()
  4412.                 valid = validDeviceURI (uri)
  4413.             except:
  4414.                 pass
  4415.             self.btnNPForward.set_sensitive(valid)
  4416.             self.btnNPBack.hide ()
  4417.         else:
  4418.             self.btnNPBack.show()
  4419.  
  4420.         self.btnNPForward.show()
  4421.         self.btnNPApply.hide()
  4422.  
  4423.         if nr == 0: # Name
  4424.             self.btnNPBack.show()
  4425.             if self.dialog_mode == "printer" or \
  4426.                     self.dialog_mode == "printer_with_uri":
  4427.                 self.btnNPForward.hide()
  4428.                 self.btnNPApply.show()
  4429.                 self.btnNPApply.set_sensitive(
  4430.                     self.mainapp.checkNPName(self.entNPName.get_text()))
  4431.             if self.dialog_mode == "class":
  4432.                 # This is the first page for the New Class dialog, so
  4433.                 # hide the Back button.
  4434.                 self.btnNPBack.hide ()
  4435.             if self.dialog_mode == "printer_with_uri" and \
  4436.                     (self.remotecupsqueue or \
  4437.                          (self.exactdrivermatch and \
  4438.                               not self.installable_options)):
  4439.                 self.btnNPBack.hide ()
  4440.         if nr == 2: # Make/PPD file
  4441.             downloadable_selected = False
  4442.             if self.rbtnNPDownloadableDriverSearch.get_active ():
  4443.                 combobox = self.cmbNPDownloadableDriverFoundPrinters
  4444.                 iter = combobox.get_active_iter ()
  4445.                 if iter and combobox.get_model ().get_value (iter, 1):
  4446.                     downloadable_selected = True
  4447.  
  4448.             self.btnNPForward.set_sensitive(bool(
  4449.                 self.rbtnNPFoomatic.get_active() or
  4450.                 self.filechooserPPD.get_filename() or
  4451.                 downloadable_selected))
  4452.             # If we have an auto-detected printer for which there was no
  4453.             # driver found, we have already the URI and so this step is
  4454.             # not needed in the wizard. This makes manufacturer?PPD selection
  4455.             # the firts step
  4456.             if self.dialog_mode == "printer_with_uri":
  4457.                 self.btnNPBack.hide()
  4458.         if nr == 3: # Model/Driver
  4459.             model, iter = self.tvNPDrivers.get_selection().get_selected()
  4460.             self.btnNPForward.set_sensitive(bool(iter))
  4461.         if nr == 4: # Class Members
  4462.             self.btnNPForward.hide()
  4463.             self.btnNPApply.show()
  4464.             self.btnNPApply.set_sensitive(
  4465.                 bool(getCurrentClassMembers(self.tvNCMembers)))
  4466.         if nr == 6: # Installable options
  4467.             if self.dialog_mode == "printer_with_uri" and \
  4468.                     self.exactdrivermatch:
  4469.                 self.btnNPBack.hide ()
  4470.         if nr == 7: # Downloadable drivers
  4471.             if self.ntbkNPDownloadableDriverProperties.get_current_page() == 1:
  4472.                 accepted = self.rbtnNPDownloadLicenseYes.get_active ()
  4473.             else:
  4474.                 treeview = self.tvNPDownloadableDrivers
  4475.                 model, iter = treeview.get_selection ().get_selected ()
  4476.                 accepted = (iter != None)
  4477.  
  4478.             self.btnNPForward.set_sensitive(accepted)
  4479.  
  4480.     def on_entNPName_changed(self, widget):
  4481.         # restrict
  4482.         text = unicode (widget.get_text())
  4483.         new_text = text
  4484.         new_text = new_text.replace("/", "")
  4485.         new_text = new_text.replace("#", "")
  4486.         new_text = new_text.replace(" ", "")
  4487.         if text!=new_text:
  4488.             widget.set_text(new_text)
  4489.         if self.dialog_mode == "printer":
  4490.             self.btnNPApply.set_sensitive(
  4491.                 self.mainapp.checkNPName(new_text))
  4492.         else:
  4493.             self.btnNPForward.set_sensitive(
  4494.                 self.mainapp.checkNPName(new_text))
  4495.  
  4496.     def fetchDevices(self, parent=None):
  4497.         debugprint ("fetchDevices")
  4498.         self.lblWait.set_markup ('<span weight="bold" size="larger">' +
  4499.                                  _('Searching') + '</span>\n\n' +
  4500.                                  _('Searching for printers'))
  4501.         if parent == None:
  4502.             parent = self.mainapp.PrintersWindow
  4503.         self.WaitWindow.set_transient_for (parent)
  4504.         self.WaitWindow.show_now ()
  4505.         while gtk.events_pending ():
  4506.             gtk.main_iteration ()
  4507.  
  4508.         debugprint ("Fetching devices")
  4509.         self.mainapp.cups._begin_operation (_("fetching device list"))
  4510.         try:
  4511.             devices = cupshelpers.getDevices(self.mainapp.cups)
  4512.         except:
  4513.             self.mainapp.cups._end_operation ()
  4514.             self.WaitWindow.hide ()
  4515.             raise
  4516.  
  4517.         self.mainapp.cups._end_operation ()
  4518.         self.WaitWindow.hide ()
  4519.         debugprint ("Got devices")
  4520.         return devices
  4521.  
  4522.     def install_hplip_plugin(self, uri):
  4523.         """
  4524.         Attempt to install a plugin using hp-plugin.
  4525.  
  4526.         @return: True if plugin not needed (or needed and installed),
  4527.         False on error.
  4528.         """
  4529.  
  4530.         if not self.HP_PLUGIN_SUPPORT:
  4531.             return True
  4532.  
  4533.         # Check necessity of the plugin
  4534.         os.environ["URI"] = uri
  4535.         cmd = 'LC_ALL=C DISPLAY= hp-info -d"${URI}"'
  4536.         debugprint (uri + ": " + cmd)
  4537.         try:
  4538.             p = subprocess.Popen (cmd, shell=True,
  4539.                                   stdin=file("/dev/null"),
  4540.                                   stdout=subprocess.PIPE,
  4541.                                   stderr=subprocess.PIPE)
  4542.             (stdout, stderr) = p.communicate ()
  4543.         except:
  4544.             # Problem executing command.
  4545.             return True # assume plugin not required
  4546.  
  4547.         plugin_needed = -1
  4548.         plugin_reason = -1
  4549.         fw_download = -1
  4550.         hplip_version = None
  4551.         for line in stdout.split ("\n"):
  4552.             if line.find ("plugin ") >= 0:
  4553.                 res = re.search ("(\d+)", line)
  4554.                 if res:
  4555.                     resg = res.groups()
  4556.                     plugin_needed = int(resg[0])
  4557.             elif line.find ("plugin-reason") >= 0:
  4558.                 res = re.search ("(\d+)", line)
  4559.                 if res:
  4560.                     resg = res.groups()
  4561.                     plugin_reason = int(resg[0])
  4562.             elif line.find ("fw-download") >= 0:
  4563.                 if line.find ("True") >= 0:
  4564.                     fw_download = 1
  4565.                 elif line.find ("False") >= 0:
  4566.                     fw_download = 0
  4567.             elif line.find ("HP Linux Imaging and Printing") >= 0:
  4568.                 res = re.search ("(\d+\.\d+\.\d+\w*)", line)
  4569.                 if res:
  4570.                     resg = res.groups()
  4571.                     hplip_version = resg[0]
  4572.             if plugin_needed >= 0 and plugin_reason >= 0 and fw_download >= 0:
  4573.                 break
  4574.         if plugin_needed <= 0 or not hplip_version:
  4575.             return True # assume plugin not required
  4576.         # Check whether the plugin is already installed
  4577.         if glob.glob("/usr/share/hplip/data/plugin/*%s*plugin*" %
  4578.                      hplip_version):
  4579.             if hplip_version.startswith("2"):
  4580.                 try:
  4581.                     f = open('/etc/hp/hplip.conf', 'r')
  4582.                     for line in f:
  4583.                         if line.strip ().startswith("plugin") and \
  4584.                                 line.strip ().endswith("1"):
  4585.                             f.close()
  4586.                             return True
  4587.                         f.close()
  4588.                 except:
  4589.                     pass
  4590.             else:
  4591.                 return True
  4592.         # Tell the user why he needs the plugin
  4593.         text = \
  4594.             _("For this printer a proprietary driver plugin from HP is available.\n")
  4595.         if plugin_needed == 1:
  4596.             text += \
  4597.                 _("The installation of the plugin is required for your printer to work.\n\n")
  4598.         elif plugin_needed == 2:
  4599.             text += \
  4600.                 _("Installing the plugin is optional, it completes or enhances the functionality\n"
  4601.                   "of your printer. Without plugin at least basic operations work.\n\n")
  4602.         if plugin_reason > 0:
  4603.             text += \
  4604.                 _("The plugin provides the following features:\n")
  4605.             if (plugin_reason & 0x1) != 0:
  4606.                 text += \
  4607.                     _(" - Printing support\n")
  4608.             if (plugin_reason & 0x2) != 0:
  4609.                 text += \
  4610.                     _(" - Faster printing\n")
  4611.             if (plugin_reason & 0x4) != 0:
  4612.                 text += \
  4613.                     _(" - Better printout quality\n")
  4614.             if (plugin_reason & 0x8) != 0:
  4615.                 text += \
  4616.                     _(" - Extra printing features\n")
  4617.             if (plugin_reason & 0x40) != 0:
  4618.                 text += \
  4619.                     _(" - Scanning support\n")
  4620.             if (plugin_reason & 0x80) != 0:
  4621.                 text += \
  4622.                     _(" - Faster scanning\n")
  4623.             if (plugin_reason & 0x100) != 0:
  4624.                 text += \
  4625.                     _(" - Better scanning image quality\n")
  4626.             if (plugin_reason & 0x800) != 0:
  4627.                 text += \
  4628.                     _(" - Faxing support\n")
  4629.             if (plugin_reason & 0x1000) != 0:
  4630.                 text += \
  4631.                     _(" - Extra fax features\n")
  4632.             if (plugin_reason & 0x4000) != 0:
  4633.                 text += \
  4634.                     _(" - Better Input/Output support\n")
  4635.             if (plugin_reason & 0x8000) != 0:
  4636.                 text += \
  4637.                     _(" - Extra user interface features\n")
  4638.             if (plugin_reason & 0x10000) != 0:
  4639.                 text += \
  4640.                     _(" - Other extra features\n")
  4641.             text += "\n"
  4642.         text += "Do you want to download and install the plugin now?\n"
  4643.         if plugin_needed == 1:
  4644.             text += "\nNOTE: The plugin is required for your printer. If you do not install it, your\nprinter will not work."
  4645.             buttons = (_("Install plugin"), 1, 
  4646.                        _("Do not set up printer"), 2,
  4647.                        _("Set up without plugin"), 3)
  4648.         else:
  4649.             buttons = (_("Yes"), 1,
  4650.                        _("No"), 3)
  4651.             
  4652.         dialog = gtk.Dialog(self.device.info,
  4653.                             self.NewPrinterWindow,
  4654.                             gtk.DIALOG_MODAL |
  4655.                             gtk.DIALOG_DESTROY_WITH_PARENT,
  4656.                             buttons)
  4657.         label = gtk.Label(text)
  4658.         dialog.vbox.pack_start(label, True, True, 0)
  4659.         label.show()
  4660.         button_clicked = dialog.run()
  4661.         dialog.destroy()
  4662.         if (button_clicked == 1):
  4663.             cmds = ("if python -c 'import PyQt4.QtGui' 2>/dev/null; then gksu -- hp-plugin -u; else exit 255; fi",
  4664.                     "gksu -- xterm -T 'HPLIP Plugin Installation' -sb -rightbar -e hp-plugin -i")
  4665.             try:
  4666.                 install_result = -1
  4667.                 for cmd in cmds:
  4668.                     p = subprocess.Popen(cmd, shell=True,
  4669.                                          stdin=file("/dev/null"),
  4670.                                          stdout=subprocess.PIPE,
  4671.                                          stderr=subprocess.PIPE)
  4672.                     (stdout, stderr) = p.communicate ()
  4673.                     while p.returncode == None:
  4674.                         while gtk.events_pending ():
  4675.                             gtk.main_iteration ()
  4676.                         time.sleep (0.1)
  4677.                         p.poll ()
  4678.                     install_result = p.returncode
  4679.                     if install_result != 255:
  4680.                         break
  4681.                 if install_result == 0:
  4682.                     return True
  4683.                 else:
  4684.                     return False
  4685.             except OSError, e:
  4686.                 debugprint ("Execution of hp-plugin failed: %s" % e)
  4687.                 return False
  4688.         elif (button_clicked == 2):
  4689.             return False
  4690.         elif (button_clicked == 3):
  4691.             return True
  4692.         return False
  4693.  
  4694.     def get_hpfax_device_id(self, faxuri):
  4695.         os.environ["URI"] = faxuri
  4696.         cmd = 'LC_ALL=C DISPLAY= hp-info -d"${URI}"'
  4697.         debugprint (faxuri + ": " + cmd)
  4698.         try:
  4699.             p = subprocess.Popen (cmd, shell=True,
  4700.                                   stdin=file("/dev/null"),
  4701.                                   stdout=subprocess.PIPE,
  4702.                                   stderr=subprocess.PIPE)
  4703.             (stdout, stderr) = p.communicate ()
  4704.         except:
  4705.             # Problem executing command.
  4706.             return None
  4707.  
  4708.         faxtype = -1
  4709.         for line in stdout.split ("\n"):
  4710.             if line.find ("fax-type") == -1:
  4711.                 continue
  4712.             res = re.search ("(\d+)", line)
  4713.             if res:
  4714.                 resg = res.groups()
  4715.                 faxtype = resg[0]
  4716.             if faxtype >= 0:
  4717.                 break
  4718.         if faxtype <= 0:
  4719.             return None
  4720.         elif faxtype == 4:
  4721.             return 'MFG:HP;MDL:Fax 2;DES:HP Fax 2;'
  4722.         else:
  4723.             return 'MFG:HP;MDL:Fax;DES:HP Fax;'
  4724.  
  4725.     def get_hplip_uri_for_network_printer(self, host, mode):
  4726.         os.environ["HOST"] = host
  4727.         if mode == "print": mod = "-c"
  4728.         elif mode == "fax": mod = "-f"
  4729.         else: mod = "-c"
  4730.         cmd = 'hp-makeuri ' + mod + ' "${HOST}"'
  4731.         debugprint (host + ": " + cmd)
  4732.         uri = None
  4733.         try:
  4734.             p = subprocess.Popen (cmd, shell=True,
  4735.                                   stdin=file("/dev/null"),
  4736.                                   stdout=subprocess.PIPE,
  4737.                                   stderr=subprocess.PIPE)
  4738.             (stdout, stderr) = p.communicate ()
  4739.         except:
  4740.             # Problem executing command.
  4741.             return None
  4742.  
  4743.         uri = stdout.strip ()
  4744.         return uri
  4745.  
  4746.     def getNetworkPrinterMakeModel(self, host=None, device=None):
  4747.         """
  4748.         Try to determine the make and model for the currently selected
  4749.         network printer, and store this in the data structure for the
  4750.         printer.
  4751.         Returns (hostname or None, uri or None).
  4752.         """
  4753.         uri = None
  4754.         if device == None:
  4755.             device = self.device
  4756.         # Determine host name/IP
  4757.         if host == None:
  4758.             s = device.uri.find ("://")
  4759.             if s != -1:
  4760.                 s += 3
  4761.                 e = device.uri[s:].find (":")
  4762.                 if e == -1: e = device.uri[s:].find ("/")
  4763.                 if e == -1: e = device.uri[s:].find ("?")
  4764.                 if e == -1: e = len (device.uri)
  4765.                 host = device.uri[s:s+e]
  4766.         # Try to get make and model via SNMP
  4767.         if host:
  4768.             os.environ["HOST"] = host
  4769.             cmd = '/usr/lib/cups/backend/snmp "${HOST}"'
  4770.             debugprint (host + ": " + cmd)
  4771.             stdout = None
  4772.             try:
  4773.                 p = subprocess.Popen (cmd, shell=True,
  4774.                                       stdin=file("/dev/null"),
  4775.                                       stdout=subprocess.PIPE,
  4776.                                       stderr=subprocess.PIPE)
  4777.                 (stdout, stderr) = p.communicate ()
  4778.             except:
  4779.                 # Problem executing command.
  4780.                 pass
  4781.  
  4782.             if stdout != None:
  4783.                 uri = re.sub("^\s*\S+\s+", "", stdout)
  4784.                 uri = re.sub("\s.*$", "", uri)
  4785.                 mm = re.sub("^\s*\S+\s+\S+\s+\"", "", stdout)
  4786.                 mm = re.sub("\"\s+.*$", "", mm)
  4787.                 if mm and mm != "": device.make_and_model = mm
  4788.         # Extract make and model and create a pseudo device ID, so
  4789.         # that a PPD/driver can be assigned to the device
  4790.         make_and_model = None
  4791.         if len (device.make_and_model) > 7:
  4792.             make_and_model = device.make_and_model
  4793.         elif len (device.info) > 7:
  4794.             make_and_model = device.info
  4795.             make_and_model = re.sub("\s*(\(|\d+\.\d+\.\d+\.\d+).*$", "", make_and_model)
  4796.         if make_and_model and not device.id:
  4797.             mk = None
  4798.             md = None
  4799.             (mk, md) = cupshelpers.ppds.ppdMakeModelSplit (make_and_model)
  4800.             device.id = "MFG:" + mk + ";MDL:" + md + ";DES:" + mk + " " + md + ";"
  4801.             device.id_dict = cupshelpers.parseDeviceID (device.id)
  4802.  
  4803.         return (host, uri)
  4804.  
  4805.     def fillDeviceTab(self, current_uri=None):
  4806.         try:
  4807.             devices = self.fetchDevices()
  4808.         except cups.IPPError, (e, msg):
  4809.             self.show_IPP_Error(e, msg)
  4810.             devices = {}
  4811.         except:
  4812.             nonfatalException()
  4813.             devices = {}
  4814.  
  4815.         if current_uri:
  4816.             if devices.has_key (current_uri):
  4817.                 current = devices.pop(current_uri)
  4818.                 current.info += _(" (Current)")
  4819.             elif devices.has_key (current_uri.replace (":9100", "")):
  4820.                 current_uri = current_uri.replace (":9100", "")
  4821.                 current = devices.pop(current_uri)
  4822.                 current.info += _(" (Current)")
  4823.             else:
  4824.                 current = cupshelpers.Device (current_uri)
  4825.                 current.info = "Current device"
  4826.  
  4827.         devices = devices.values()
  4828.  
  4829.         for device in devices:
  4830.             if device.type == "socket":
  4831.                 # Remove default port to more easily find duplicate URIs
  4832.                 device.uri = device.uri.replace (":9100", "")
  4833.  
  4834.         # Map generic URIs to something canonical
  4835.         def replace_generic (device):
  4836.             if device.uri == "hp:/no_device_found":
  4837.                 device.uri = "hp"
  4838.             elif device.uri == "hpfax:/no_device_found":
  4839.                 device.uri = "hpfax"
  4840.             return device
  4841.  
  4842.         devices = map (replace_generic, devices)
  4843.  
  4844.         # For HPLIP, only locally-connected devices are detected.  Add
  4845.         # the generic URI to allow network devices to be searched for.
  4846.         for each in devices:
  4847.             if each.type == "hp":
  4848.                 if each.uri != "hp":
  4849.                     # We know the hp backend is available because it has
  4850.                     # found a locally-connected device.
  4851.                     hp = cupshelpers.Device ("hp",
  4852.                                              **{'device-class': 'network',
  4853.                                                 'device-info':
  4854.                                                     _("HP Printer (HPLIP)")})
  4855.                     devices.append (hp)
  4856.                     break
  4857.                 else:
  4858.                     # The hp backend is in the device list but as a
  4859.                     # generic driver with no devices found.  It
  4860.                     # reports "direct" as its class in this case, but
  4861.                     # ought to report "network".
  4862.                     each.device_class = 'network'
  4863.  
  4864.         # Mark duplicate URIs for deletion
  4865.         for i in range (len (devices) - 1):
  4866.             for j in range (i + 1, len (devices)):
  4867.                 device1 = devices[i]
  4868.                 device2 = devices[j]
  4869.                 if device1.uri == "delete" or device2.uri == "delete":
  4870.                     continue
  4871.                 if device1.uri == device2.uri:
  4872.                     # Keep the one with the longer (better) device ID
  4873.                     if (not device1.id):
  4874.                         device1.uri = "delete"
  4875.                     elif (not device2.id):
  4876.                         device2.uri = "delete"
  4877.                     elif (len (device1.id) < len (device2.id)):
  4878.                         device1.uri = "delete"
  4879.                     else:
  4880.                         device2.uri = "delete"
  4881.         devices = filter(lambda x: x.uri not in ("hpfax",
  4882.                                                  "hal", "beh",
  4883.                                                  "scsi", "http", "delete"),
  4884.                          devices)
  4885.         self.devices = []
  4886.         for device in devices:
  4887.             physicaldevice = PhysicalDevice (device)
  4888.             try:
  4889.                 i = self.devices.index (physicaldevice)
  4890.                 self.devices[i].add_device (device)
  4891.             except ValueError:
  4892.                 self.devices.append (physicaldevice)
  4893.  
  4894.         self.devices.sort()
  4895.         other = cupshelpers.Device('', **{'device-info' :_("Other")})
  4896.         self.devices.append (PhysicalDevice (other))
  4897.         device_select_index = 0
  4898.         if current_uri:
  4899.             current_device = PhysicalDevice (current)
  4900.             try:
  4901.                 i = self.devices.index (current_device)
  4902.                 self.devices[i].add_device (current)
  4903.                 device_select_index = i
  4904.                 devs = self.devices[i].get_devices ()
  4905.             except ValueError:
  4906.                 self.devices.insert(0, current_device)
  4907.  
  4908.         model = gtk.TreeStore (gobject.TYPE_STRING,   # device-info
  4909.                                gobject.TYPE_PYOBJECT, # PhysicalDevice obj
  4910.                                gobject.TYPE_BOOLEAN)  # Separator?
  4911.         network_iter = model.append (None, row=[_("Network Printer"),
  4912.                                                 None,
  4913.                                                 False])
  4914.         network_dict = { 'device-class': 'network',
  4915.                          'device-info': _("Find Network Printer") }
  4916.         network = cupshelpers.Device ('network', **network_dict)
  4917.         find_nw_iter = model.append (network_iter,
  4918.                                      row=[network_dict['device-info'],
  4919.                                           PhysicalDevice (network), False])
  4920.         model.insert_after (network_iter, find_nw_iter, row=['', None, True])
  4921.         self.devices_find_nw_iter = find_nw_iter
  4922.         self.tvNPDevices.set_model (model)
  4923.  
  4924.         i = 0
  4925.         device_select_path = None
  4926.         for device in self.devices:
  4927.             devs = device.get_devices ()
  4928.             network = devs[0].device_class == 'network'
  4929.             row=[device.get_info (), device, False]
  4930.             if network:
  4931.                 if devs[0].uri != devs[0].type:
  4932.                     # An actual network printer device.  Put this at the top.
  4933.                     iter = model.insert_before (network_iter, find_nw_iter,
  4934.                                                 row=row)
  4935.  
  4936.                     # If this is the currently selected device we need
  4937.                     # to expand the "Network Printer" row so that it
  4938.                     # is visible.
  4939.                     if device_select_index == i:
  4940.                         network_path = model.get_path (network_iter)
  4941.                         self.tvNPDevices.expand_row (network_path, False)
  4942.                 else:
  4943.                     # Just a method of finding one.
  4944.                     iter = model.append (network_iter, row=row)
  4945.             else:
  4946.                 iter = model.insert_before(None, network_iter, row=row)
  4947.  
  4948.             if device_select_index == i:
  4949.                 device_select_path = model.get_path (iter)
  4950.                 self.tvNPDevices.scroll_to_cell (device_select_path,
  4951.                                                  row_align=0.5)
  4952.                 column = self.tvNPDevices.get_column (0)
  4953.                 self.tvNPDevices.set_cursor (device_select_path, column)
  4954.  
  4955.             i += 1
  4956.  
  4957.         connection_select_path = 0
  4958.         if current_uri:
  4959.             model = self.tvNPDeviceURIs.get_model ()
  4960.             iter = model.get_iter_first ()
  4961.             i = 0
  4962.             while iter:
  4963.                 dev = model.get_value (iter, 1)
  4964.                 if current_uri == dev.uri:
  4965.                     connection_select_path = i
  4966.                     break
  4967.  
  4968.                 iter = model.iter_next (iter)
  4969.                 i += 1
  4970.  
  4971.         column = self.tvNPDeviceURIs.get_column (0)
  4972.         self.tvNPDeviceURIs.set_cursor (connection_select_path, column)
  4973.  
  4974.     def on_entNPTDevice_changed(self, entry):
  4975.         self.setNPButtons()
  4976.  
  4977.     ## SMB browsing
  4978.  
  4979.     def browse_smb_hosts(self):
  4980.         """Initialise the SMB tree store."""
  4981.         store = self.smb_store
  4982.         store.clear ()
  4983.         self.busy(self.SMBBrowseDialog)
  4984.         class X:
  4985.             pass
  4986.         dummy = X()
  4987.         dummy.smbc_type = pysmb.smbc.PRINTER_SHARE
  4988.         dummy.name = _('Scanning...')
  4989.         dummy.comment = ''
  4990.         store.append(None, [dummy])
  4991.         while gtk.events_pending ():
  4992.             gtk.main_iteration ()
  4993.  
  4994.         debug = 0
  4995.         if get_debugging ():
  4996.             debug = 10
  4997.         smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
  4998.         ctx = pysmb.smbc.Context (debug=debug,
  4999.                                   auth_fn=smbc_auth.callback)
  5000.         entries = None
  5001.         try:
  5002.             while smbc_auth.perform_authentication () > 0:
  5003.                 try:
  5004.                     entries = ctx.opendir ("smb://").getdents ()
  5005.                 except Exception, e:
  5006.                     smbc_auth.failed (e)
  5007.         except RuntimeError, (e, s):
  5008.             if e != errno.ENOENT:
  5009.                 debugprint ("Runtime error: %s" % repr ((e, s)))
  5010.         except:
  5011.             nonfatalException ()
  5012.  
  5013.         store.clear ()
  5014.         if entries:
  5015.             for entry in entries:
  5016.                 if entry.smbc_type in [pysmb.smbc.WORKGROUP,
  5017.                                        pysmb.smbc.SERVER]:
  5018.                     iter = store.append (None, [entry])
  5019.                     i = store.append (iter)
  5020.  
  5021.         specified_uri = SMBURI (uri=self.entSMBURI.get_text ())
  5022.         (group, host, share, user, password) = specified_uri.separate ()
  5023.         if len (host) > 0:
  5024.             # The user has specified a server before clicking Browse.
  5025.             # Append the server as a top-level entry.
  5026.             class FakeEntry:
  5027.                 pass
  5028.             toplevel = FakeEntry ()
  5029.             toplevel.smbc_type = pysmb.smbc.SERVER
  5030.             toplevel.name = host
  5031.             toplevel.comment = ''
  5032.             iter = store.append (None, [toplevel])
  5033.             i = store.append (iter)
  5034.  
  5035.             # Now expand it.
  5036.             path = store.get_path (iter)
  5037.             self.tvSMBBrowser.expand_row (path, 0)
  5038.  
  5039.         self.ready(self.SMBBrowseDialog)
  5040.  
  5041.         if store.get_iter_first () == None:
  5042.             self.SMBBrowseDialog.hide ()
  5043.             show_info_dialog (_("No Print Shares"),
  5044.                               _("There were no print shares found.  "
  5045.                                 "Please check that the Samba service is "
  5046.                                 "marked as trusted in your firewall "
  5047.                                 "configuration.") + '\n\n' +
  5048.                               TEXT_start_firewall_tool,
  5049.                               parent=self.NewPrinterWindow)
  5050.  
  5051.     def smb_select_function (self, path):
  5052.         """Don't allow this path to be selected unless it is a leaf."""
  5053.         iter = self.smb_store.get_iter (path)
  5054.         return not self.smb_store.iter_has_child (iter)
  5055.  
  5056.     def smbbrowser_cell_share (self, column, cell, model, iter):
  5057.         entry = model.get_value (iter, 0)
  5058.         share = ''
  5059.         if entry != None:
  5060.             share = entry.name
  5061.         cell.set_property ('text', share)
  5062.  
  5063.     def smbbrowser_cell_comment (self, column, cell, model, iter):
  5064.         entry = model.get_value (iter, 0)
  5065.         comment = ''
  5066.         if entry != None:
  5067.             comment = entry.comment
  5068.         cell.set_property ('text', comment)
  5069.  
  5070.     def on_tvSMBBrowser_row_activated (self, view, path, column):
  5071.         """Handle double-clicks in the SMB tree view."""
  5072.         store = self.smb_store
  5073.         iter = store.get_iter (path)
  5074.         entry = store.get_value (iter, 0)
  5075.         if entry and entry.smbc_type == pysmb.smbc.PRINTER_SHARE:
  5076.             # This is a share, not a host.
  5077.             self.btnSMBBrowseOk.clicked ()
  5078.             return
  5079.  
  5080.         if view.row_expanded (path):
  5081.             view.collapse_row (path)
  5082.         else:
  5083.             self.on_tvSMBBrowser_row_expanded (view, iter, path)
  5084.  
  5085.     def on_tvSMBBrowser_row_expanded (self, view, iter, path):
  5086.         """Handler for expanding a row in the SMB tree view."""
  5087.         model = view.get_model ()
  5088.         entry = model.get_value (iter, 0)
  5089.         if entry == None:
  5090.             return
  5091.  
  5092.         if entry.smbc_type == pysmb.smbc.WORKGROUP:
  5093.             # Workgroup
  5094.             # Be careful though: if there is a server with the
  5095.             # same name as the workgroup we will get a list of its
  5096.             # shares, not the workgroup's servers.
  5097.             try:
  5098.                 if self.expanding_row:
  5099.                     return
  5100.             except:
  5101.                 self.expanding_row = 1
  5102.  
  5103.             self.busy (self.SMBBrowseDialog)
  5104.             uri = "smb://%s/" % entry.name
  5105.             debug = 0
  5106.             if get_debugging ():
  5107.                 debug = 10
  5108.             smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
  5109.             ctx = pysmb.smbc.Context (debug=debug,
  5110.                                       auth_fn=smbc_auth.callback)
  5111.             entries = []
  5112.             try:
  5113.                 while smbc_auth.perform_authentication () > 0:
  5114.                     try:
  5115.                         entries = ctx.opendir (uri).getdents ()
  5116.                     except Exception, e:
  5117.                         smbc_auth.failed (e)
  5118.             except RuntimeError, (e, s):
  5119.                 if e != errno.ENOENT:
  5120.                     debugprint ("Runtime error: %s" % repr ((e, s)))
  5121.             except:
  5122.                 nonfatalException()
  5123.  
  5124.             while model.iter_has_child (iter):
  5125.                 i = model.iter_nth_child (iter, 0)
  5126.                 model.remove (i)
  5127.  
  5128.             for entry in entries:
  5129.                 if entry.smbc_type in [pysmb.smbc.SERVER,
  5130.                                        pysmb.smbc.PRINTER_SHARE]:
  5131.                     i = model.append (iter, [entry])
  5132.                 if entry.smbc_type == pysmb.smbc.SERVER:
  5133.                     n = model.append (i)
  5134.  
  5135.             view.expand_row (path, 0)
  5136.             del self.expanding_row
  5137.             self.ready (self.SMBBrowseDialog)
  5138.  
  5139.         elif entry.smbc_type == pysmb.smbc.SERVER:
  5140.             # Server
  5141.             try:
  5142.                 if self.expanding_row:
  5143.                     return
  5144.             except:
  5145.                 self.expanding_row = 1
  5146.  
  5147.             self.busy (self.SMBBrowseDialog)
  5148.             uri = "smb://%s/" % entry.name
  5149.             debug = 0
  5150.             if get_debugging ():
  5151.                 debug = 10
  5152.             smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
  5153.             ctx = pysmb.smbc.Context (debug=debug,
  5154.                                       auth_fn=smbc_auth.callback)
  5155.             shares = []
  5156.             try:
  5157.                 while smbc_auth.perform_authentication () > 0:
  5158.                     try:
  5159.                         shares = ctx.opendir (uri).getdents ()
  5160.                     except Exception, e:
  5161.                         smbc_auth.failed (e)
  5162.             except RuntimeError, (e, s):
  5163.                 if e != errno.EACCES and e != errno.EPERM:
  5164.                     debugprint ("Runtime error: %s" % repr ((e, s)))
  5165.             except:
  5166.                 nonfatalException()
  5167.  
  5168.             while model.iter_has_child (iter):
  5169.                 i = model.iter_nth_child (iter, 0)
  5170.                 model.remove (i)
  5171.  
  5172.             for share in shares:
  5173.                 if share.smbc_type == pysmb.smbc.PRINTER_SHARE:
  5174.                     i = model.append (iter, [share])
  5175.                     debugprint (repr (share))
  5176.  
  5177.             view.expand_row (path, 0)
  5178.             del self.expanding_row
  5179.             self.ready (self.SMBBrowseDialog)
  5180.  
  5181.     def on_entSMBURI_changed (self, ent):
  5182.         uri = ent.get_text ()
  5183.         (group, host, share, user, password) = SMBURI (uri=uri).separate ()
  5184.         if user:
  5185.             self.entSMBUsername.set_text (user)
  5186.         if password:
  5187.             self.entSMBPassword.set_text (password)
  5188.         if user or password:
  5189.             uri = SMBURI (group=group, host=host, share=share).get_uri ()
  5190.             ent.set_text(uri)
  5191.             self.rbtnSMBAuthSet.set_active(True)
  5192.         elif self.entSMBUsername.get_text () == '':
  5193.             self.rbtnSMBAuthPrompt.set_active(True)
  5194.  
  5195.         self.btnSMBVerify.set_sensitive(bool(uri))
  5196.         self.setNPButtons ()
  5197.  
  5198.     def on_tvSMBBrowser_cursor_changed(self, widget):
  5199.         store, iter = self.tvSMBBrowser.get_selection().get_selected()
  5200.         is_share = False
  5201.         if iter:
  5202.             entry = store.get_value (iter, 0)
  5203.             if entry:
  5204.                 is_share = entry.smbc_type == pysmb.smbc.PRINTER_SHARE
  5205.  
  5206.         self.btnSMBBrowseOk.set_sensitive(iter != None and is_share)
  5207.  
  5208.     def on_btnSMBBrowse_clicked(self, button):
  5209.         self.btnSMBBrowseOk.set_sensitive(False)
  5210.         self.SMBBrowseDialog.show()
  5211.         self.browse_smb_hosts()
  5212.  
  5213.     def on_btnSMBBrowseOk_clicked(self, button):
  5214.         store, iter = self.tvSMBBrowser.get_selection().get_selected()
  5215.         is_share = False
  5216.         if iter:
  5217.             entry = store.get_value (iter, 0)
  5218.             if entry:
  5219.                 is_share = entry.smbc_type == pysmb.smbc.PRINTER_SHARE
  5220.  
  5221.         if not iter or not is_share:
  5222.             self.SMBBrowseDialog.hide()
  5223.             return
  5224.  
  5225.         parent_iter = store.iter_parent (iter)
  5226.         domain_iter = store.iter_parent (parent_iter)
  5227.         share = store.get_value (iter, 0)
  5228.         host = store.get_value (parent_iter, 0)
  5229.         if domain_iter:
  5230.             group = store.get_value (domain_iter, 0).name
  5231.         else:
  5232.             group = ''
  5233.         uri = SMBURI (group=group,
  5234.                       host=host.name,
  5235.                       share=share.name).get_uri ()
  5236.  
  5237.         self.entSMBUsername.set_text ('')
  5238.         self.entSMBPassword.set_text ('')
  5239.         self.entSMBURI.set_text (uri)
  5240.  
  5241.         self.SMBBrowseDialog.hide()
  5242.  
  5243.     def on_btnSMBBrowseCancel_clicked(self, widget, *args):
  5244.         self.SMBBrowseDialog.hide()
  5245.  
  5246.     def on_btnSMBBrowseRefresh_clicked(self, button):
  5247.         self.browse_smb_hosts()
  5248.  
  5249.     def on_rbtnSMBAuthSet_toggled(self, widget):
  5250.         self.tblSMBAuth.set_sensitive(widget.get_active())
  5251.  
  5252.     def on_btnSMBVerify_clicked(self, button):
  5253.         uri = self.entSMBURI.get_text ()
  5254.         (group, host, share, u, p) = SMBURI (uri=uri).separate ()
  5255.         user = ''
  5256.         passwd = ''
  5257.         reason = None
  5258.         auth_set = self.rbtnSMBAuthSet.get_active()
  5259.         if auth_set:
  5260.             user = self.entSMBUsername.get_text ()
  5261.             passwd = self.entSMBPassword.get_text ()
  5262.  
  5263.         accessible = False
  5264.         canceled = False
  5265.         self.busy ()
  5266.         try:
  5267.             debug = 0
  5268.             if get_debugging ():
  5269.                 debug = 10
  5270.  
  5271.             if auth_set:
  5272.                 # No prompting.
  5273.                 def do_auth (svr, shr, wg, un, pw):
  5274.                     return (group, user, passwd)
  5275.                 ctx = pysmb.smbc.Context (debug=debug, auth_fn=do_auth)
  5276.                 f = ctx.open ("smb://%s/%s" % (host, share),
  5277.                               os.O_RDWR, 0777)
  5278.                 accessible = True
  5279.             else:
  5280.                 # May need to prompt.
  5281.                 smbc_auth = pysmb.AuthContext (self.NewPrinterWindow,
  5282.                                                workgroup=group,
  5283.                                                user=user,
  5284.                                                passwd=passwd)
  5285.                 ctx = pysmb.smbc.Context (debug=debug,
  5286.                                           auth_fn=smbc_auth.callback)
  5287.                 while smbc_auth.perform_authentication () > 0:
  5288.                     try:
  5289.                         f = ctx.open ("smb://%s/%s" % (host, share),
  5290.                                       os.O_RDWR, 0777)
  5291.                         accessible = True
  5292.                     except Exception, e:
  5293.                         smbc_auth.failed (e)
  5294.  
  5295.                 if not accessible:
  5296.                     canceled = True
  5297.         except RuntimeError, (e, s):
  5298.             debugprint ("Error accessing share: %s" % repr ((e, s)))
  5299.             reason = s
  5300.         except:
  5301.             nonfatalException()
  5302.         self.ready ()
  5303.  
  5304.         if accessible:
  5305.             show_info_dialog (_("Print Share Verified"),
  5306.                               _("This print share is accessible."),
  5307.                               parent=self.NewPrinterWindow)
  5308.             return
  5309.  
  5310.         if not canceled:
  5311.             text = _("This print share is not accessible.")
  5312.             if reason:
  5313.                 text = reason
  5314.             show_error_dialog (_("Print Share Inaccessible"), text,
  5315.                                parent=self.NewPrinterWindow)
  5316.  
  5317.     ### IPP Browsing
  5318.     def update_IPP_URI_label(self):
  5319.         hostname = self.entNPTIPPHostname.get_text ()
  5320.         queue = self.entNPTIPPQueuename.get_text ()
  5321.         valid = len (hostname) > 0 and queue != '/printers/'
  5322.  
  5323.         if valid:
  5324.             uri = "ipp://%s%s" % (hostname, queue)
  5325.             self.lblIPPURI.set_text (uri)
  5326.             self.lblIPPURI.show ()
  5327.             self.entNPTIPPQueuename.show ()
  5328.         else:
  5329.             self.lblIPPURI.hide ()
  5330.  
  5331.         self.btnIPPVerify.set_sensitive (valid)
  5332.         self.setNPButtons ()
  5333.  
  5334.     def on_entNPTIPPHostname_changed(self, ent):
  5335.         valid = len (ent.get_text ()) > 0
  5336.         self.btnIPPFindQueue.set_sensitive (valid)
  5337.         self.update_IPP_URI_label ()
  5338.  
  5339.     def on_entNPTIPPQueuename_changed(self, ent):
  5340.         self.update_IPP_URI_label ()
  5341.  
  5342.     def on_btnIPPFindQueue_clicked(self, button):
  5343.         self.btnIPPBrowseOk.set_sensitive(False)
  5344.         self.IPPBrowseDialog.show()
  5345.         self.browse_ipp_queues()
  5346.  
  5347.     def on_btnIPPVerify_clicked(self, button):
  5348.         uri = self.lblIPPURI.get_text ()
  5349.         match = re.match ("(ipp|https?)://([^/]+)(.*)/([^/]*)", uri)
  5350.         verified = False
  5351.         if match:
  5352.             oldserver = cups.getServer ()
  5353.             try:
  5354.                 c = cups.Connection (host=match.group (2))
  5355.                 attributes = c.getPrinterAttributes (uri = uri)
  5356.                 verified = True
  5357.             except cups.IPPError, (e, msg):
  5358.                 debugprint ("Failed to get attributes: %s (%d)" % (msg, e))
  5359.             except:
  5360.                 nonfatalException ()
  5361.             cups.setServer (oldserver)
  5362.         else:
  5363.             debugprint (uri)
  5364.  
  5365.         if verified:
  5366.             show_info_dialog (_("Print Share Verified"),
  5367.                               _("This print share is accessible."),
  5368.                               parent=self.NewPrinterWindow)
  5369.         else:
  5370.             show_error_dialog (_("Inaccessible"),
  5371.                                _("This print share is not accessible."),
  5372.                                self.NewPrinterWindow)
  5373.  
  5374.     def browse_ipp_queues(self):
  5375.         if not self.ipp_lock.acquire(0):
  5376.             return
  5377.         thread.start_new_thread(self.browse_ipp_queues_thread, ())
  5378.  
  5379.     def browse_ipp_queues_thread(self):
  5380.         host = None
  5381.         gtk.gdk.threads_enter()
  5382.         try:
  5383.             store = self.ipp_store
  5384.             store.clear ()
  5385.             store.append(None, (_('Scanning...'), '', None))
  5386.             try:
  5387.                 self.busy(self.IPPBrowseDialog)
  5388.             except:
  5389.                 nonfatalException()
  5390.  
  5391.             host = self.entNPTIPPHostname.get_text()
  5392.         except:
  5393.             nonfatalException()
  5394.         gtk.gdk.threads_leave()
  5395.  
  5396.         oldserver = cups.getServer ()
  5397.         printers = classes = {}
  5398.         failed = False
  5399.         port = 631
  5400.         if host != None:
  5401.             (host, port) = urllib.splitnport (host, defport=port)
  5402.  
  5403.         try:
  5404.             c = cups.Connection (host=host, port=port)
  5405.             printers = c.getPrinters ()
  5406.             del c
  5407.         except cups.IPPError, (e, m):
  5408.             debugprint ("IPP browser: %s" % m)
  5409.             failed = True
  5410.         except:
  5411.             nonfatalException()
  5412.             failed = True
  5413.         cups.setServer (oldserver)
  5414.  
  5415.         gtk.gdk.threads_enter()
  5416.         try:
  5417.             store.clear ()
  5418.             for printer, dict in printers.iteritems ():
  5419.                 iter = store.append (None)
  5420.                 store.set_value (iter, 0, printer)
  5421.                 store.set_value (iter, 1, dict.get ('printer-location', ''))
  5422.                 store.set_value (iter, 2, dict)
  5423.  
  5424.             if len (printers) + len (classes) == 0:
  5425.                 # Display 'No queues' dialog
  5426.                 if failed:
  5427.                     title = _("Not possible")
  5428.                     text = (_("It is not possible to obtain a list of queues "
  5429.                               "from '%s'.") % host + '\n\n' +
  5430.                             _("Obtaining a list of queues is a CUPS extension "
  5431.                               "to IPP.  Network printers do not support it."))
  5432.                 else:
  5433.                     title = _("No queues")
  5434.                     text = _("There are no queues available.")
  5435.  
  5436.                 self.IPPBrowseDialog.hide ()
  5437.                 show_error_dialog (title, text, self.NewPrinterWindow)
  5438.  
  5439.             try:
  5440.                 self.ready(self.IPPBrowseDialog)
  5441.             except:
  5442.                 nonfatalException()
  5443.  
  5444.             self.ipp_lock.release()
  5445.         except:
  5446.             nonfatalException()
  5447.         gtk.gdk.threads_leave()
  5448.  
  5449.     def on_tvIPPBrowser_cursor_changed(self, widget):
  5450.         self.btnIPPBrowseOk.set_sensitive(True)
  5451.  
  5452.     def on_btnIPPBrowseOk_clicked(self, button):
  5453.         store, iter = self.tvIPPBrowser.get_selection().get_selected()
  5454.         self.IPPBrowseDialog.hide()
  5455.         queue = store.get_value (iter, 0)
  5456.         dict = store.get_value (iter, 2)
  5457.         self.entNPTIPPQueuename.set_text (queue)
  5458.         self.entNPTIPPQueuename.show()
  5459.         uri = dict.get('printer-uri-supported', 'ipp')
  5460.         match = re.match ("(ipp|https?)://([^/]+)(.*)", uri)
  5461.         if match:
  5462.             self.entNPTIPPHostname.set_text (match.group (2))
  5463.             self.entNPTIPPQueuename.set_text (match.group (3))
  5464.  
  5465.         self.lblIPPURI.set_text (uri)
  5466.         self.lblIPPURI.show()
  5467.         self.setNPButtons()
  5468.  
  5469.     def on_btnIPPBrowseCancel_clicked(self, widget, *args):
  5470.         self.IPPBrowseDialog.hide()
  5471.  
  5472.     def on_btnIPPBrowseRefresh_clicked(self, button):
  5473.         self.browse_ipp_queues()
  5474.  
  5475.     def on_expNPDeviceURIs_expanded (self, widget, UNUSED):
  5476.         # When the expanded is not expanded we want its packing to be
  5477.         # 'expand = false' so that it aligns at the bottom (it packs
  5478.         # to the end of its vbox).  But when it is expanded we'd like
  5479.         # it to expand with the window.
  5480.         #
  5481.         # Adjust its 'expand' packing state depending on whether the
  5482.         # widget is expanded.
  5483.  
  5484.         parent = widget.get_parent ()
  5485.         (expand, fill,
  5486.          padding, pack_type) = parent.query_child_packing (widget)
  5487.         expand = widget.get_expanded ()
  5488.         parent.set_child_packing (widget, expand, fill,
  5489.                                   padding, pack_type)
  5490.  
  5491.     def device_row_separator_fn (self, model, iter):
  5492.         return model.get_value (iter, 2)
  5493.  
  5494.     def device_row_activated (self, view, path, column):
  5495.         if view.row_expanded (path):
  5496.             view.collapse_row (path)
  5497.         else:
  5498.             view.expand_row (path, False)
  5499.  
  5500.     def device_select_function (self, path):
  5501.         """
  5502.         Allow this path to be selected as long as there
  5503.         is a device associated with it.
  5504.         """
  5505.         model = self.tvNPDevices.get_model ()
  5506.         iter = model.get_iter (path)
  5507.         return model.get_value (iter, 1) != None
  5508.  
  5509.     def on_tvNPDevices_cursor_changed(self, widget):
  5510.         path, column = widget.get_cursor ()
  5511.         if path == None:
  5512.             return
  5513.  
  5514.         model = widget.get_model ()
  5515.         iter = model.get_iter (path)
  5516.         physicaldevice = model.get_value (iter, 1)
  5517.         if physicaldevice == None:
  5518.             return
  5519.         for device in physicaldevice.get_devices ():
  5520.             if device.type == "parallel":
  5521.                 device.menuentry = _("Parallel Port")
  5522.             elif device.type == "serial":
  5523.                 device.menuentry = _("Serial Port")
  5524.             elif device.type == "usb":
  5525.                 device.menuentry = _("USB")
  5526.             elif device.type == "hp":
  5527.                 device.menuentry = _("HP Linux Imaging and Printing (HPLIP)")
  5528.             elif device.type == "hpfax":
  5529.                 device.menuentry = _("Fax") + " - " + \
  5530.                     _("HP Linux Imaging and Printing (HPLIP)")
  5531.             elif device.type == "hal":
  5532.                 device.menuentry = _("Hardware Abstraction Layer (HAL)")
  5533.             elif device.type == "socket":
  5534.                 device.menuentry = _("AppSocket/HP JetDirect")
  5535.             elif device.type == "lpd":
  5536.                 device.menuentry = _("LPD/LPR")
  5537.             elif device.type == "smb":
  5538.                 device.menuentry = _("Windows Printer via SAMBA")
  5539.             elif device.type == "ipp":
  5540.                 device.menuentry = _("IPP")
  5541.             elif device.type == "http":
  5542.                 device.menuentry = _("HTTP")
  5543.             else:
  5544.                 device.menuentry = device.uri
  5545.  
  5546.         model = gtk.ListStore (str,                    # URI description
  5547.                                gobject.TYPE_PYOBJECT)  # cupshelpers.Device
  5548.         self.tvNPDeviceURIs.set_model (model)
  5549.  
  5550.         # If this is a network device, check whether HPLIP can drive it.
  5551.         if physicaldevice.get_data ('checked-hplip') != True:
  5552.             hp_drivable = False
  5553.             is_network = False
  5554.             device_dict = { 'device-class': 'network' }
  5555.             for device in physicaldevice.get_devices ():
  5556.                 if device.type == "hp":
  5557.                     # We already know that HPLIP can drive this device.
  5558.                     hp_drivable = True
  5559.                     break
  5560.                 elif device.type in ["socket", "lpd", "ipp"]:
  5561.                     # This is a network printer.
  5562.                     (scheme, rest) = urllib.splittype (device.uri)
  5563.                     (hostport, rest) = urllib.splithost (rest)
  5564.                     if hostport != None:
  5565.                         (host, port) = urllib.splitport (hostport)
  5566.                         is_network = True
  5567.                         self.getNetworkPrinterMakeModel(host=host,
  5568.                                                         device=device)
  5569.                         device_dict['device-info'] = device.info
  5570.                         device_dict['device-make-and-model'] = (device.
  5571.                                                                 make_and_model)
  5572.                         device_dict['device-id'] = device.id
  5573.  
  5574.             if not hp_drivable and is_network:
  5575.                 hplipuri = self.get_hplip_uri_for_network_printer (host,
  5576.                                                                    "print")
  5577.                 if hplipuri:
  5578.                     dev = cupshelpers.Device (hplipuri, **device_dict)
  5579.                     dev.menuentry = "HP Linux Imaging and Printing (HPLIP)"
  5580.                     physicaldevice.add_device (dev)
  5581.  
  5582.                     # Now check to see if we can also send faxes using
  5583.                     # this device.
  5584.                     faxuri = self.get_hplip_uri_for_network_printer (host,
  5585.                                                                      "fax")
  5586.                     if faxuri:
  5587.                         faxdevid = self.get_hpfax_device_id (faxuri)
  5588.                         device_dict['device-id'] = faxdevid
  5589.                         device_dict['device-info'] = _("Fax")
  5590.                         faxdev = cupshelpers.Device (faxuri, **device_dict)
  5591.                         faxdev.menuentry = _("Fax") + " - " + \
  5592.                             "HP Linux Imaging and Printing (HPLIP)"
  5593.                         physicaldevice.add_device (faxdev)
  5594.  
  5595.                 physicaldevice.set_data ('checked-hplip', True)
  5596.  
  5597.         # Fill the list of connections for this device.
  5598.         n = 0
  5599.         for device in physicaldevice.get_devices ():
  5600.             model.append ((device.menuentry, device))
  5601.             n += 1
  5602.         column = self.tvNPDeviceURIs.get_column (0)
  5603.         self.tvNPDeviceURIs.set_cursor (0, column)
  5604.         if n > 1:
  5605.             self.expNPDeviceURIs.show_all ()
  5606.         else:
  5607.             self.expNPDeviceURIs.hide ()
  5608.  
  5609.     def on_tvNPDeviceURIs_cursor_changed(self, widget):
  5610.         model, iter = widget.get_selection().get_selected()
  5611.         device = model.get_value(iter, 1)
  5612.         self.device = device
  5613.         self.lblNPDeviceDescription.set_text ('')
  5614.         page = self.new_printer_device_tabs.get(device.type, 1)
  5615.         if device.type == "hp" and device.uri != "hp":
  5616.             page = 0
  5617.  
  5618.         self.ntbkNPType.set_current_page(page)
  5619.  
  5620.         location = ''
  5621.         type = device.type
  5622.         url = device.uri.split(":", 1)[-1]
  5623.         if page == 0:
  5624.             # This is the "no options" page, with just a label to describe
  5625.             # the selected device.
  5626.             if device.type == "parallel":
  5627.                 text = _("A printer connected to the parallel port.")
  5628.             elif device.type == "usb":
  5629.                 text = _("A printer connected to a USB port.")
  5630.             elif device.type == "hp":
  5631.                 text = _("HPLIP software driving a printer, "
  5632.                          "or the printer function of a multi-function device.")
  5633.             elif device.type == "hpfax":
  5634.                 text = _("HPLIP software driving a fax machine, "
  5635.                          "or the fax function of a multi-function device.")
  5636.             elif device.type == "hal":
  5637.                 text = _("Local printer detected by the "
  5638.                          "Hardware Abstraction Layer (HAL).")
  5639.             else:
  5640.                 text = device.uri
  5641.  
  5642.             self.lblNPDeviceDescription.set_text (text)
  5643.         elif device.type=="socket":
  5644.             (scheme, rest) = urllib.splittype (device.uri)
  5645.             host = ''
  5646.             port = 9100
  5647.             if scheme == "socket":
  5648.                 (hostport, rest) = urllib.splithost (rest)
  5649.                 (host, port) = urllib.splitnport (hostport, defport=port)
  5650.                 debugprint ("socket: host is %s, port is %s" % (host,
  5651.                                                                 repr (port)))
  5652.                 location = host
  5653.             self.entNPTDirectJetHostname.set_text (host)
  5654.             self.entNPTDirectJetPort.set_text (str (port))
  5655.         elif device.type=="serial":
  5656.             if not device.is_class:
  5657.                 options = device.uri.split("?")[1]
  5658.                 options = options.split("+")
  5659.                 option_dict = {}
  5660.                 for option in options:
  5661.                     name, value = option.split("=")
  5662.                     option_dict[name] = value
  5663.  
  5664.                 for widget, name, optionvalues in (
  5665.                     (self.cmbNPTSerialBaud, "baud", None),
  5666.                     (self.cmbNPTSerialBits, "bits", None),
  5667.                     (self.cmbNPTSerialParity, "parity",
  5668.                      ["none", "odd", "even"]),
  5669.                     (self.cmbNPTSerialFlow, "flow",
  5670.                      ["none", "soft", "hard", "hard"])):
  5671.                     if option_dict.has_key(name): # option given in URI?
  5672.                         if optionvalues is None: # use text in widget
  5673.                             model = widget.get_model()
  5674.                             iter = model.get_iter_first()
  5675.                             nr = 0
  5676.                             while iter:
  5677.                                 value = model.get(iter,0)[0]
  5678.                                 if value == option_dict[name]:
  5679.                                     widget.set_active(nr)
  5680.                                     break
  5681.                                 iter = model.iter_next(iter)
  5682.                                 nr += 1
  5683.                         else: # use optionvalues
  5684.                             nr = optionvalues.index(
  5685.                                 option_dict[name])
  5686.                             widget.set_active(nr+1) # compensate "Default"
  5687.                     else:
  5688.                         widget.set_active(0)
  5689.  
  5690.         # XXX FILL TABS FOR VALID DEVICE URIs
  5691.         elif device.type in ("ipp", "http"):
  5692.             if (device.uri.startswith ("ipp:") or
  5693.                 device.uri.startswith ("http:")):
  5694.                 match = re.match ("(ipp|https?)://([^/]+)(.*)", device.uri)
  5695.                 if match:
  5696.                     server = match.group (2)
  5697.                     printer = match.group (3)
  5698.                 else:
  5699.                     server = ""
  5700.                     printer = ""
  5701.  
  5702.                 self.entNPTIPPHostname.set_text(server)
  5703.                 self.entNPTIPPQueuename.set_text(printer)
  5704.                 self.lblIPPURI.set_text(device.uri)
  5705.                 self.lblIPPURI.show()
  5706.                 self.entNPTIPPQueuename.show()
  5707.                 location = server
  5708.             else:
  5709.                 self.entNPTIPPHostname.set_text('')
  5710.                 self.entNPTIPPQueuename.set_text('/printers/')
  5711.                 self.entNPTIPPQueuename.show()
  5712.                 self.lblIPPURI.hide()
  5713.         elif device.type=="lpd":
  5714.             if device.uri.startswith ("lpd"):
  5715.                 host = device.uri[6:]
  5716.                 i = host.find ("/")
  5717.                 if i != -1:
  5718.                     printer = host[i + 1:]
  5719.                     host = host[:i]
  5720.                 else:
  5721.                     printer = ""
  5722.                 self.cmbentNPTLpdHost.child.set_text (host)
  5723.                 self.cmbentNPTLpdQueue.child.set_text (printer)
  5724.                 location = host
  5725.         elif device.uri == "lpd":
  5726.             pass
  5727.         elif device.uri == "smb":
  5728.             self.entSMBURI.set_text('')
  5729.             self.btnSMBVerify.set_sensitive(False)
  5730.         elif device.type == "smb":
  5731.             self.entSMBUsername.set_text ('')
  5732.             self.entSMBPassword.set_text ('')
  5733.             self.entSMBURI.set_text(device.uri[6:])
  5734.             self.btnSMBVerify.set_sensitive(True)
  5735.         elif device.uri == "hp":
  5736.             self.lblHPURI.set_text ('')
  5737.         else:
  5738.             self.entNPTDevice.set_text(device.uri)
  5739.  
  5740.         try:
  5741.             if len (location) == 0 and self.device.device_class == "direct":
  5742.                 # Set location to the name of this host.
  5743.                 u = os.uname ()
  5744.                 location = u[1]
  5745.  
  5746.             # Pre-fill location field.
  5747.             self.entNPLocation.set_text (location)
  5748.         except:
  5749.             nonfatalException ()
  5750.  
  5751.         self.setNPButtons()
  5752.  
  5753.     def on_btnNPTLpdProbe_clicked(self, button):
  5754.         # read hostname, probe, fill printer names
  5755.         hostname = self.cmbentNPTLpdHost.get_active_text()
  5756.         server = probe_printer.LpdServer(hostname)
  5757.         printers = server.probe()
  5758.         model = self.cmbentNPTLpdQueue.get_model()
  5759.         model.clear()
  5760.         for printer in printers:
  5761.             self.cmbentNPTLpdQueue.append_text(printer)
  5762.         if printers:
  5763.             self.cmbentNPTLpdQueue.set_active(0)
  5764.  
  5765.     def on_entNPTHPHostname_changed(self, ent):
  5766.         self.lblHPURI.set_text ('')
  5767.         s = ent.get_text ()
  5768.         self.btnHPFindQueue.set_sensitive (len (s) > 0)
  5769.         self.setNPButtons ()
  5770.  
  5771.     def on_btnHPFindQueue_clicked(self, button):
  5772.         host = self.entNPTHPHostname.get_text ()
  5773.  
  5774.         # Check whether the device is supported by HPLIP
  5775.         hplipuri = self.get_hplip_uri_for_network_printer(host, "print")
  5776.         if hplipuri == None or hplipuri == '':
  5777.             show_error_dialog (_("No Print Shares"),
  5778.                                _("HPLIP cannot find the device."),
  5779.                                self.NewPrinterWindow)
  5780.             self.entNPTHPHostname.grab_focus ()
  5781.             return
  5782.  
  5783.         self.lblHPURI.set_text (hplipuri)
  5784.         s = hplipuri.find ("/usb/")
  5785.         if s == -1:
  5786.             s = hplipuri.find ("/par/")
  5787.  
  5788.         if s == -1:
  5789.             s = hplipuri.find ("/net/")
  5790.  
  5791.         if s != -1:
  5792.             s += 5
  5793.             e = hplipuri[s:].find ("?")
  5794.             if e == -1:
  5795.                 e = len (hplipuri)
  5796.  
  5797.             mdl = hplipuri[s:s+e].replace ("_", " ")
  5798.             if mdl.startswith ("hp ") or mdl.startswith ("HP "):
  5799.                 mdl = mdl[3:]
  5800.                 self.device.make_and_model = "HP " + mdl
  5801.                 id = "MFG:HP;MDL:%s;DES:HP %s;" % (mdl, mdl)
  5802.                 self.device.id = id
  5803.                 self.device.id_dict = cupshelpers.parseDeviceID (id)
  5804.  
  5805.     ### Find Network Printer
  5806.     def on_entNPTNetworkHostname_changed(self, ent):
  5807.         s = ent.get_text ()
  5808.         self.btnNetworkFind.set_sensitive (len (s) > 0)
  5809.         self.setNPButtons ()
  5810.  
  5811.     def on_btnNetworkFind_clicked(self, button):
  5812.         host = self.entNPTNetworkHostname.get_text ()
  5813.         uri = self.get_hplip_uri_for_network_printer (host, "print")
  5814.         device_dict = { 'device-class': 'network' }
  5815.         new_device = cupshelpers.Device ('', **device_dict)
  5816.         if uri:
  5817.             new_device = cupshelpers.Device (uri, **device_dict)
  5818.  
  5819.         (host, uri) = self.getNetworkPrinterMakeModel (host=host,
  5820.                                                        device=new_device)
  5821.         if uri:
  5822.             new_device.uri = uri
  5823.  
  5824.         debugprint ("New device: %s" % new_device)
  5825.         if not new_device.uri:
  5826.             show_error_dialog (_("Not Found"),
  5827.                                _("No printer was found at that address."),
  5828.                                parent=self.NewPrinterWindow)
  5829.         else:
  5830.             model = self.tvNPDevices.get_model ()
  5831.             dev = PhysicalDevice (new_device)
  5832.             iter = model.insert_before (None, self.devices_find_nw_iter,
  5833.                                         row=[dev.get_info (), dev, False])
  5834.             path = model.get_path (iter)
  5835.             self.tvNPDevices.set_cursor (path)
  5836.     ###
  5837.  
  5838.     def getDeviceURI(self):
  5839.         type = self.device.type
  5840.         if type == "socket": # DirectJet
  5841.             host = self.entNPTDirectJetHostname.get_text()
  5842.             port = self.entNPTDirectJetPort.get_text()
  5843.             device = "socket://" + host
  5844.             if host and port:
  5845.                 device = device + ':' + port
  5846.         elif type in ("http", "ipp"): # IPP
  5847.             if self.lblIPPURI.get_property('visible'):
  5848.                 device = self.lblIPPURI.get_text()
  5849.             else:
  5850.                 device = "ipp"
  5851.         elif type == "lpd": # LPD
  5852.             host = self.cmbentNPTLpdHost.get_active_text()
  5853.             printer = self.cmbentNPTLpdQueue.get_active_text()
  5854.             device = "lpd://" + host
  5855.             if printer:
  5856.                 device = device + "/" + printer
  5857.         elif type == "parallel": # Parallel
  5858.             device = self.device.uri
  5859.         elif type == "scsi": # SCSII
  5860.             device = ""
  5861.         elif type == "serial": # Serial
  5862.             options = []
  5863.             for widget, name, optionvalues in (
  5864.                 (self.cmbNPTSerialBaud, "baud", None),
  5865.                 (self.cmbNPTSerialBits, "bits", None),
  5866.                 (self.cmbNPTSerialParity, "parity",
  5867.                  ("none", "odd", "even")),
  5868.                 (self.cmbNPTSerialFlow, "flow",
  5869.                  ("none", "soft", "hard", "hard"))):
  5870.                 nr = widget.get_active()
  5871.                 if nr:
  5872.                     if optionvalues is not None:
  5873.                         option = optionvalues[nr-1]
  5874.                     else:
  5875.                         option = widget.get_active_text()
  5876.                     options.append(name + "=" + option)
  5877.             options = "+".join(options)
  5878.             device =  self.device.uri.split("?")[0] #"serial:/dev/ttyS%s"
  5879.             if options:
  5880.                 device = device + "?" + options
  5881.         elif type == "smb":
  5882.             uri = self.entSMBURI.get_text ()
  5883.             (group, host, share, u, p) = SMBURI (uri=uri).separate ()
  5884.             user = ''
  5885.             password = ''
  5886.             if self.rbtnSMBAuthSet.get_active ():
  5887.                 user = self.entSMBUsername.get_text ()
  5888.                 password = self.entSMBPassword.get_text ()
  5889.             uri = SMBURI (group=group, host=host, share=share,
  5890.                           user=user, password=password).get_uri ()
  5891.             device = "smb://" + uri
  5892.         elif self.device.uri == "hp":
  5893.             device = self.lblHPURI.get_text ()
  5894.         elif not self.device.is_class:
  5895.             device = self.device.uri
  5896.         else:
  5897.             device = self.entNPTDevice.get_text()
  5898.         return device
  5899.  
  5900.     # PPD
  5901.  
  5902.     def on_rbtnNPFoomatic_toggled(self, widget):
  5903.         rbtn1 = self.rbtnNPFoomatic.get_active()
  5904.         rbtn2 = self.rbtnNPPPD.get_active()
  5905.         rbtn3 = self.rbtnNPDownloadableDriverSearch.get_active()
  5906.         self.tvNPMakes.set_sensitive(rbtn1)
  5907.         self.filechooserPPD.set_sensitive(rbtn2)
  5908.  
  5909.         if rbtn1:
  5910.             page = 0
  5911.         if rbtn2:
  5912.             page = 1
  5913.         if rbtn3:
  5914.             page = 2
  5915.         self.ntbkPPDSource.set_current_page (page)
  5916.  
  5917.         if not rbtn3 and self.openprinting_query_handle:
  5918.             # Need to cancel a search in progress.
  5919.             self.openprinting.cancelOperation (self.openprinting_query_handle)
  5920.             self.openprinting_query_handle = None
  5921.             self.btnNPDownloadableDriverSearch_label.set_text (_("Search"))
  5922.             # Clear printer list.
  5923.             model = gtk.ListStore (str, str)
  5924.             combobox = self.cmbNPDownloadableDriverFoundPrinters
  5925.             combobox.set_model (model)
  5926.             combobox.set_sensitive (False)
  5927.  
  5928.         for widget in [self.entNPDownloadableDriverSearch,
  5929.                        self.cmbNPDownloadableDriverFoundPrinters]:
  5930.             widget.set_sensitive(rbtn3)
  5931.         self.btnNPDownloadableDriverSearch.\
  5932.             set_sensitive (rbtn3 and (self.openprinting_query_handle == None))
  5933.  
  5934.         self.setNPButtons()
  5935.  
  5936.     def on_filechooserPPD_selection_changed(self, widget):
  5937.         self.setNPButtons()
  5938.  
  5939.     def on_btnNPDownloadableDriverSearch_clicked(self, widget):
  5940.         if self.openprinting_query_handle != None:
  5941.             self.openprinting.cancelOperation (self.openprinting_query_handle)
  5942.             self.openprinting_query_handle = None
  5943.  
  5944.         widget.set_sensitive (False)
  5945.         label = self.btnNPDownloadableDriverSearch_label
  5946.         label.set_text (_("Searching"))
  5947.         searchterm = self.entNPDownloadableDriverSearch.get_text ()
  5948.         self.openprinting_query_handle = \
  5949.             self.openprinting.searchPrinters (searchterm,
  5950.                                               self.openprinting_printers_found)
  5951.         self.cmbNPDownloadableDriverFoundPrinters.set_sensitive (False)
  5952.  
  5953.     def openprinting_printers_found (self, status, user_data, printers):
  5954.         self.openprinting_query_handle = None
  5955.         button = self.btnNPDownloadableDriverSearch
  5956.         label = self.btnNPDownloadableDriverSearch_label
  5957.         gtk.gdk.threads_enter ()
  5958.         try:
  5959.             label.set_text (_("Search"))
  5960.             button.set_sensitive (True)
  5961.             if status != 0:
  5962.                 # Should report error.
  5963.                 print printers
  5964.                 print traceback.extract_tb(printers[2], limit=None)
  5965.                 gtk.gdk.threads_leave ()
  5966.                 return
  5967.  
  5968.             model = gtk.ListStore (str, str)
  5969.             if len (printers) != 1:
  5970.                 if len (printers) > 1:
  5971.                     first = _("-- Select from search results --")
  5972.                 else:
  5973.                     first = _("-- No matches found --")
  5974.  
  5975.                 iter = model.append (None)
  5976.                 model.set_value (iter, 0, first)
  5977.                 model.set_value (iter, 1, None)
  5978.  
  5979.             sorted_list = []
  5980.             for id, name in printers.iteritems ():
  5981.                 sorted_list.append ((id, name))
  5982.  
  5983.             sorted_list.sort (lambda x, y: cups.modelSort (x[1], y[1]))
  5984.             sought = self.entNPDownloadableDriverSearch.get_text ().lower ()
  5985.             select_index = 0
  5986.             for id, name in sorted_list:
  5987.                 iter = model.append (None)
  5988.                 model.set_value (iter, 0, name)
  5989.                 model.set_value (iter, 1, id)
  5990.                 if name.lower () == sought:
  5991.                     select_index = model.get_path (iter)[0]
  5992.             combobox = self.cmbNPDownloadableDriverFoundPrinters
  5993.             combobox.set_model (model)
  5994.             combobox.set_active (select_index)
  5995.             combobox.set_sensitive (True)
  5996.             self.setNPButtons ()
  5997.         except:
  5998.             nonfatalException()
  5999.         gtk.gdk.threads_leave ()
  6000.  
  6001.     def on_cmbNPDownloadableDriverFoundPrinters_changed(self, widget):
  6002.         self.setNPButtons ()
  6003.  
  6004.         if self.openprinting_query_handle != None:
  6005.             self.openprinting.cancelOperation (self.openprinting_query_handle)
  6006.             self.openprinting_query_handle = None
  6007.             self.drivers_lock.release()
  6008.  
  6009.         model = widget.get_model ()
  6010.         iter = widget.get_active_iter ()
  6011.         if iter:
  6012.             id = model.get_value (iter, 1)
  6013.         else:
  6014.             id = None
  6015.  
  6016.         if id == None:
  6017.             return
  6018.  
  6019.         # A model has been selected, so start the query to find out
  6020.         # which drivers are available.
  6021.         debugprint ("Query drivers for %s" % id)
  6022.         self.drivers_lock.acquire(0)
  6023.         extra_options = dict()
  6024.         if self.DOWNLOADABLE_ONLYPPD:
  6025.             extra_options['onlyppdfiles'] = '1'
  6026.         self.openprinting_query_handle = \
  6027.             self.openprinting.listDrivers (id,
  6028.                                            self.openprinting_drivers_found,
  6029.                                            extra_options=extra_options)
  6030.  
  6031.     def openprinting_drivers_found (self, status, user_data, drivers):
  6032.         if status != 0:
  6033.             # Should report error.
  6034.             print drivers
  6035.             print traceback.extract_tb(drivers[2], limit=None)
  6036.             return
  6037.  
  6038.         self.openprinting_query_handle = None
  6039.         self.downloadable_drivers = drivers
  6040.         debugprint ("Drivers query completed: %s" % drivers.keys ())
  6041.         self.drivers_lock.release()
  6042.  
  6043.     def fillDownloadableDrivers(self):
  6044.         # Clear out the properties.
  6045.         self.lblNPDownloadableDriverSupplier.set_text ('')
  6046.         self.lblNPDownloadableDriverLicense.set_text ('')
  6047.         self.lblNPDownloadableDriverDescription.set_text ('')
  6048.         self.lblNPDownloadableDriverSupportContacts.set_text ('')
  6049.         self.rbtnNPDownloadLicenseNo.set_active (True)
  6050.         self.frmNPDownloadableDriverLicenseTerms.hide ()
  6051.  
  6052.         drivers = self.downloadable_drivers
  6053.         model = gtk.ListStore (str,                     # driver name
  6054.                                gobject.TYPE_PYOBJECT)   # driver data
  6055.         recommended_iter = None
  6056.         first_iter = None
  6057.         for driver in drivers.values ():
  6058.             iter = model.append (None)
  6059.             if first_iter == None:
  6060.                 first_iter = iter
  6061.  
  6062.             model.set_value (iter, 0, driver['name'])
  6063.             model.set_value (iter, 1, driver)
  6064.  
  6065.             if driver['recommended']:
  6066.                 recommended_iter = iter
  6067.  
  6068.         if recommended_iter == None:
  6069.             recommended_iter = first_iter
  6070.  
  6071.         treeview = self.tvNPDownloadableDrivers
  6072.         treeview.set_model (model)
  6073.         if recommended_iter != None:
  6074.             treeview.get_selection ().select_iter (recommended_iter)
  6075.  
  6076.     def on_rbtnNPDownloadLicense_toggled(self, widget):
  6077.         self.setNPButtons ()
  6078.  
  6079.     # PPD from foomatic
  6080.  
  6081.     def fillMakeList(self):
  6082.         makes = self.ppds.getMakes()
  6083.         model = self.tvNPMakes.get_model()
  6084.         model.clear()
  6085.         found = False
  6086.         for make in makes:
  6087.             iter = model.append((make,))
  6088.             if make.lower()==self.auto_make.lower():
  6089.                 path = model.get_path(iter)
  6090.                 self.tvNPMakes.set_cursor (path)
  6091.                 self.tvNPMakes.scroll_to_cell(path, None,
  6092.                                               True, 0.5, 0.5)
  6093.                 found = True
  6094.  
  6095.         if not found:
  6096.             self.tvNPMakes.set_cursor (0,)
  6097.             self.tvNPMakes.scroll_to_cell(0, None, True, 0.0, 0.0)
  6098.  
  6099.         # Also pre-fill the OpenPrinting.org search box.
  6100.         search = ''
  6101.         if self.auto_make != None:
  6102.             search += self.auto_make
  6103.             if self.auto_model != None:
  6104.                 search += " " + self.auto_model
  6105.         self.entNPDownloadableDriverSearch.set_text (search)
  6106.  
  6107.     def on_tvNPMakes_cursor_changed(self, tvNPMakes):
  6108.         path, column = tvNPMakes.get_cursor()
  6109.         if path != None:
  6110.             model = tvNPMakes.get_model ()
  6111.             iter = model.get_iter (path)
  6112.             self.NPMake = model.get(iter, 0)[0]
  6113.             self.fillModelList()
  6114.  
  6115.     def fillModelList(self):
  6116.         models = self.ppds.getModels(self.NPMake)
  6117.         model = self.tvNPModels.get_model()
  6118.         model.clear()
  6119.         selected = False
  6120.         for pmodel in models:
  6121.             iter = model.append((pmodel,))
  6122.             if self.NPMake.lower()==self.auto_make.lower() and \
  6123.                     pmodel.lower()==self.auto_model.lower():
  6124.                 path = model.get_path(iter)
  6125.                 self.tvNPModels.set_cursor (path)
  6126.                 self.tvNPModels.scroll_to_cell(path, None,
  6127.                                                True, 0.5, 0.5)
  6128.                 selected = True
  6129.         if not selected:
  6130.             self.tvNPModels.set_cursor (0,)
  6131.             self.tvNPModels.scroll_to_cell(0, None, True, 0.0, 0.0)
  6132.         self.tvNPModels.columns_autosize()
  6133.  
  6134.     def fillDriverList(self, pmake, pmodel):
  6135.         self.NPModel = pmodel
  6136.         model = self.tvNPDrivers.get_model()
  6137.         model.clear()
  6138.  
  6139.         ppds = self.ppds.getInfoFromModel(pmake, pmodel)
  6140.  
  6141.         self.NPDrivers = self.ppds.orderPPDNamesByPreference(ppds.keys(),
  6142.                                              self.jockey_installed_files) 
  6143.         for i in range (len(self.NPDrivers)):
  6144.             ppd = ppds[self.NPDrivers[i]]
  6145.             driver = ppd["ppd-make-and-model"]
  6146.             driver = driver.replace(" (recommended)", "")
  6147.  
  6148.             try:
  6149.                 lpostfix = " [%s]" % ppd["ppd-natural-language"]
  6150.                 driver += lpostfix
  6151.             except KeyError:
  6152.                 pass
  6153.  
  6154.             if i == 0:
  6155.                 iter = model.append ((driver + _(" (recommended)"),))
  6156.                 path = model.get_path (iter)
  6157.                 self.tvNPDrivers.get_selection().select_path(path)
  6158.                 self.tvNPDrivers.scroll_to_cell(path, None, True, 0.5, 0.0)
  6159.             else:
  6160.                 model.append((driver, ))
  6161.         self.tvNPDrivers.columns_autosize()
  6162.  
  6163.     def NPDriversTooltips(self, model, path, col):
  6164.         drivername = self.NPDrivers[path[0]]
  6165.         ppddict = self.ppds.getInfoFromPPDName(drivername)
  6166.         markup = ppddict['ppd-make-and-model']
  6167.         if (drivername.startswith ("foomatic:")):
  6168.             markup += " "
  6169.             markup += _("This PPD is generated by foomatic.")
  6170.         return markup
  6171.  
  6172.     def on_tvNPModels_cursor_changed(self, widget):
  6173.         path, column = widget.get_cursor()
  6174.         if path != None:
  6175.             model = widget.get_model ()
  6176.             iter = model.get_iter (path)
  6177.             pmodel = model.get(iter, 0)[0]
  6178.             self.fillDriverList(self.NPMake, pmodel)
  6179.             self.on_tvNPDrivers_cursor_changed(self.tvNPDrivers)
  6180.  
  6181.     def on_tvNPDrivers_cursor_changed(self, widget):
  6182.         self.setNPButtons()
  6183.  
  6184.     def on_tvNPDownloadableDrivers_cursor_changed(self, widget):
  6185.         model, iter = widget.get_selection ().get_selected ()
  6186.         if not iter:
  6187.             path, column = widget.get_cursor()
  6188.             iter = model.get_iter (path)
  6189.         driver = model.get_value (iter, 1)
  6190.         import pprint
  6191.         pprint.pprint (driver)
  6192.         self.ntbkNPDownloadableDriverProperties.set_current_page(1)
  6193.         supplier = driver.get('supplier', _("OpenPrinting"))
  6194.         vendor = self.cbNPDownloadableDriverSupplierVendor
  6195.         active = driver['manufacturersupplied']
  6196.  
  6197.         def set_protect_active (widget, active):
  6198.             widget.set_active (active)
  6199.             widget.set_data ('protect_active', active)
  6200.  
  6201.         set_protect_active (vendor, active)
  6202.         self.lblNPDownloadableDriverSupplier.set_text (supplier)
  6203.  
  6204.         license = driver.get('license', _("Distributable"))
  6205.         patents = self.cbNPDownloadableDriverLicensePatents
  6206.         free = self.cbNPDownloadableDriverLicenseFree
  6207.         set_protect_active (patents, driver['patents'])
  6208.         set_protect_active (free, driver['freesoftware'])
  6209.         self.lblNPDownloadableDriverLicense.set_text (license)
  6210.  
  6211.         description = driver.get('shortdescription', _("None"))
  6212.         self.lblNPDownloadableDriverDescription.set_markup (description)
  6213.  
  6214.         functionality = driver['functionality']
  6215.         for field in ["Graphics", "LineArt", "Photo", "Text"]:
  6216.             key = field.lower ()
  6217.             value = None
  6218.             hs = self.__dict__.get ("hsDownloadableDriverPerf%s" % field)
  6219.             unknown = self.__dict__.get ("lblDownloadableDriverPerf%sUnknown"
  6220.                                          % field)
  6221.             if functionality.has_key (key):
  6222.                 if hs:
  6223.                     try:
  6224.                         value = int (functionality[key])
  6225.                         hs.set_value (value)
  6226.                         hs.show_all ()
  6227.                         unknown.hide ()
  6228.                     except:
  6229.                         pass
  6230.  
  6231.             if value == None:
  6232.                 hs.hide ()
  6233.                 unknown.show_all ()
  6234.         supportcontacts = ""
  6235.         if driver.has_key ('supportcontacts'):
  6236.             for supportentry in driver['supportcontacts']:
  6237.                 if supportentry['name']:
  6238.                     supportcontact = " - " + supportentry['name']
  6239.                     supportcontact_extra = ""
  6240.                     if supportentry['url']:
  6241.                         supportcontact_extra = supportcontact_extra + \
  6242.                             supportentry['url']
  6243.                     if supportentry['level']:
  6244.                         if supportcontact_extra:
  6245.                             supportcontact_extra = supportcontact_extra + _(", ")
  6246.                         supportcontact_extra = supportcontact_extra + \
  6247.                             supportentry['level']
  6248.                     if supportcontact_extra:
  6249.                         supportcontact = supportcontact + \
  6250.                             _("\n(%s)") % supportcontact_extra
  6251.                         if supportcontacts:
  6252.                             supportcontacts = supportcontacts + "\n"
  6253.                         supportcontacts = supportcontacts + supportcontact
  6254.         if not supportcontacts:
  6255.             supportcontacts = _("No support contacts known")
  6256.         self.lblNPDownloadableDriverSupportContacts.set_text (supportcontacts)
  6257.         if driver.has_key ('licensetext'):
  6258.             self.frmNPDownloadableDriverLicenseTerms.show ()
  6259.             terms = driver.get('licensetext', _("Not specified."))
  6260.             self.tvNPDownloadableDriverLicense.get_buffer ().set_text (terms)
  6261.         else:
  6262.             self.frmNPDownloadableDriverLicenseTerms.hide ()
  6263.         if not driver['nonfreesoftware'] and not driver['patents']:
  6264.             self.rbtnNPDownloadLicenseYes.set_active (True)
  6265.             self.rbtnNPDownloadLicenseYes.hide ()
  6266.             self.rbtnNPDownloadLicenseNo.hide ()
  6267.         else:
  6268.             self.rbtnNPDownloadLicenseNo.set_active (True)
  6269.             self.rbtnNPDownloadLicenseYes.show ()
  6270.             self.rbtnNPDownloadLicenseNo.show ()
  6271.             self.frmNPDownloadableDriverLicenseTerms.show ()
  6272.             terms = driver.get('licensetext', _("Not specified."))
  6273.             self.tvNPDownloadableDriverLicense.get_buffer ().set_text (terms)
  6274.  
  6275.         self.setNPButtons()
  6276.  
  6277.     def getNPPPD(self):
  6278.         try:
  6279.             if self.rbtnNPFoomatic.get_active():
  6280.                 model, iter = self.tvNPDrivers.get_selection().get_selected()
  6281.                 nr = model.get_path(iter)[0]
  6282.                 ppd = self.NPDrivers[nr]
  6283.             elif self.rbtnNPPPD.get_active():
  6284.                 ppd = cups.PPD(self.filechooserPPD.get_filename())
  6285.             else:
  6286.                 # PPD of the driver downloaded from OpenPrinting XXX
  6287.                 treeview = self.tvNPDownloadableDrivers
  6288.                 model, iter = treeview.get_selection ().get_selected ()
  6289.                 driver = model.get_value (iter, 1)
  6290.                 if driver.has_key ('ppds'):
  6291.                     # Only need to download a PPD.
  6292.                     if (len(driver['ppds']) > 0):
  6293.                         file_to_download = driver['ppds'][0]
  6294.                         debugprint ("ppd file to download [" + file_to_download+ "]")
  6295.                         file_to_download = file_to_download.strip()
  6296.                         if (len(file_to_download) > 0):
  6297.                             ppdurlobj = urllib.urlopen(file_to_download)
  6298.                             ppdcontent = ppdurlobj.read()
  6299.                             ppdurlobj.close()
  6300.                             (tmpfd, ppdname) = tempfile.mkstemp()
  6301.                             debugprint(ppdname)
  6302.                             ppdfile = os.fdopen(tmpfd, 'w')
  6303.                             ppdfile.write(ppdcontent)
  6304.                             ppdfile.close()
  6305.                             ppd = cups.PPD(ppdname)
  6306.                             os.unlink(ppdname)
  6307.  
  6308.         except RuntimeError, e:
  6309.             debugprint ("RuntimeError: " + str(e))
  6310.             if self.rbtnNPFoomatic.get_active():
  6311.                 # Foomatic database problem of some sort.
  6312.                 err_title = _('Database error')
  6313.                 err_text = _("The '%s' driver cannot be "
  6314.                              "used with printer '%s %s'.")
  6315.                 model, iter = (self.tvNPDrivers.get_selection().
  6316.                                get_selected())
  6317.                 nr = model.get_path(iter)[0]
  6318.                 driver = self.NPDrivers[nr]
  6319.                 if driver.startswith ("gutenprint"):
  6320.                     # This printer references some XML that is not
  6321.                     # installed by default.  Point the user at the
  6322.                     # package they need to install.
  6323.                     err = _("You will need to install the '%s' package "
  6324.                             "in order to use this driver.") % \
  6325.                             "gutenprint-foomatic"
  6326.                 else:
  6327.                     err = err_text % (driver, self.NPMake, self.NPModel)
  6328.             elif self.rbtnNPPPD.get_active():
  6329.                 # This error came from trying to open the PPD file.
  6330.                 err_title = _('PPD error')
  6331.                 filename = self.filechooserPPD.get_filename()
  6332.                 err = _('Failed to read PPD file.  Possible reason '
  6333.                         'follows:') + '\n'
  6334.                 try:
  6335.                     # We want this to be in the current natural language,
  6336.                     # so we intentionally don't set LC_ALL=C here.
  6337.                     p = subprocess.Popen (['/usr/bin/cupstestppd',
  6338.                                            '-rvv', filename],
  6339.                                           stdin=file("/dev/null"),
  6340.                                           stdout=subprocess.PIPE,
  6341.                                           stderr=subprocess.PIPE)
  6342.                     (stdout, stderr) = p.communicate ()
  6343.                     err += stdout
  6344.                 except:
  6345.                     # Problem executing command.
  6346.                     raise
  6347.             else:
  6348.                 # Failed to get PPD downloaded from OpenPrinting XXX
  6349.                 err_title = _('Downloadable drivers')
  6350.                 err = _("Failed to download PPD.")
  6351.  
  6352.             show_error_dialog (err_title, err, self.NewPrinterWindow)
  6353.             return None
  6354.  
  6355.         debugprint("ppd: " + repr(ppd))
  6356.         if isinstance(ppd, str) or isinstance(ppd, unicode):
  6357.             self.mainapp.cups._begin_operation (_("fetching PPD"))
  6358.             try:
  6359.                 if ppd != "raw":
  6360.                     f = self.mainapp.cups.getServerPPD(ppd)
  6361.                     ppd = cups.PPD(f)
  6362.                     os.unlink(f)
  6363.             except RuntimeError:
  6364.                 nonfatalException()
  6365.                 debugprint ("libcups from CUPS 1.3 not available: never mind")
  6366.             except cups.IPPError:
  6367.                 nonfatalException()
  6368.                 debugprint ("CUPS 1.3 server not available: never mind")
  6369.  
  6370.             self.mainapp.cups._end_operation ()
  6371.  
  6372.         return ppd
  6373.  
  6374.     # Installable Options
  6375.  
  6376.     def fillNPInstallableOptions(self):
  6377.         debugprint ("Examining installable options")
  6378.         self.installable_options = False
  6379.         self.options = { }
  6380.  
  6381.         container = self.vbNPInstallOptions
  6382.         for child in container.get_children():
  6383.             container.remove(child)
  6384.  
  6385.         if not self.ppd:
  6386.             l = gtk.Label(_("No Installable Options"))
  6387.             container.add(l)
  6388.             l.show()
  6389.             debugprint ("No PPD so no installable options")
  6390.             return
  6391.  
  6392.         # build option tabs
  6393.         for group in self.ppd.optionGroups:
  6394.             if group.name != "InstallableOptions":
  6395.                 continue
  6396.             self.installable_options = True
  6397.  
  6398.             table = gtk.Table(1, 3, False)
  6399.             table.set_col_spacings(6)
  6400.             table.set_row_spacings(6)
  6401.             container.add(table)
  6402.             rows = 0
  6403.  
  6404.             for nr, option in enumerate(group.options):
  6405.                 if option.keyword == "PageRegion":
  6406.                     continue
  6407.                 rows += 1
  6408.                 table.resize (rows, 3)
  6409.                 o = OptionWidget(option, self.ppd, self)
  6410.                 table.attach(o.conflictIcon, 0, 1, nr, nr+1, 0, 0, 0, 0)
  6411.  
  6412.                 hbox = gtk.HBox()
  6413.                 if o.label:
  6414.                     a = gtk.Alignment (0.5, 0.5, 1.0, 1.0)
  6415.                     a.set_padding (0, 0, 0, 6)
  6416.                     a.add (o.label)
  6417.                     table.attach(a, 1, 2, nr, nr+1, gtk.FILL, 0, 0, 0)
  6418.                     table.attach(hbox, 2, 3, nr, nr+1, gtk.FILL, 0, 0, 0)
  6419.                 else:
  6420.                     table.attach(hbox, 1, 3, nr, nr+1, gtk.FILL, 0, 0, 0)
  6421.                 hbox.pack_start(o.selector, False)
  6422.                 self.options[option.keyword] = o
  6423.         if not self.installable_options:
  6424.             l = gtk.Label(_("No Installable Options"))
  6425.             container.add(l)
  6426.             l.show()
  6427.         self.scrNPInstallableOptions.hide()
  6428.         self.scrNPInstallableOptions.show_all()
  6429.  
  6430.  
  6431.     # Create new Printer
  6432.     def on_btnNPApply_clicked(self, widget):
  6433.         if self.dialog_mode in ("class", "printer", "printer_with_uri"):
  6434.             name = unicode (self.entNPName.get_text())
  6435.             location = unicode (self.entNPLocation.get_text())
  6436.             info = unicode (self.entNPDescription.get_text())
  6437.         else:
  6438.             name = self.mainapp.printer.name
  6439.  
  6440.         # Whether to check for missing drivers.
  6441.         check = False
  6442.         checkppd = None
  6443.         ppd = self.ppd
  6444.  
  6445.         if self.dialog_mode == "class":
  6446.             members = getCurrentClassMembers(self.tvNCMembers)
  6447.             try:
  6448.                 for member in members:
  6449.                     self.mainapp.cups.addPrinterToClass(member, name)
  6450.             except cups.IPPError, (e, msg):
  6451.                 self.show_IPP_Error(e, msg)
  6452.                 return
  6453.         elif self.dialog_mode == "printer" or \
  6454.                 self.dialog_mode == "printer_with_uri":
  6455.             uri = None
  6456.             if self.device.uri:
  6457.                 uri = self.device.uri
  6458.             else:
  6459.                 uri = self.getDeviceURI()
  6460.             if not self.ppd: # XXX needed?
  6461.                 # Go back to previous page to re-select driver.
  6462.                 self.nextNPTab(-1)
  6463.                 return
  6464.  
  6465.             # write Installable Options to ppd
  6466.             for option in self.options.itervalues():
  6467.                 option.writeback()
  6468.  
  6469.             self.busy (self.NewPrinterWindow)
  6470.             while gtk.events_pending ():
  6471.                 gtk.main_iteration ()
  6472.             self.mainapp.cups._begin_operation (_("adding printer %s") % name)
  6473.             try:
  6474.                 if isinstance(ppd, str) or isinstance(ppd, unicode):
  6475.                     self.mainapp.cups.addPrinter(name, ppdname=ppd,
  6476.                          device=uri, info=info, location=location)
  6477.                     check = True
  6478.                 elif ppd is None: # raw queue
  6479.                     self.mainapp.cups.addPrinter(name, device=uri,
  6480.                                          info=info, location=location)
  6481.                 else:
  6482.                     cupshelpers.setPPDPageSize(ppd, self.language[0])
  6483.                     self.mainapp.cups.addPrinter(name, ppd=ppd,
  6484.                          device=uri, info=info, location=location)
  6485.                     check = True
  6486.                     checkppd = ppd
  6487.             except cups.IPPError, (e, msg):
  6488.                 self.ready (self.NewPrinterWindow)
  6489.                 self.show_IPP_Error(e, msg)
  6490.                 self.mainapp.cups._end_operation()
  6491.                 return
  6492.             except:
  6493.                 self.ready (self.NewPrinterWindow)
  6494.                 self.mainapp.cups._end_operation()
  6495.                 fatalException (1)
  6496.             self.mainapp.cups._end_operation()
  6497.             self.ready (self.NewPrinterWindow)
  6498.         if self.dialog_mode in ("class", "printer", "printer_with_uri"):
  6499.             self.mainapp.cups._begin_operation (_("modifying printer %s") %
  6500.                                                 name)
  6501.             try:
  6502.                 cupshelpers.activateNewPrinter (self.mainapp.cups, name)
  6503.                 self.mainapp.cups.setPrinterLocation(name, location)
  6504.                 self.mainapp.cups.setPrinterInfo(name, info)
  6505.             except cups.IPPError, (e, msg):
  6506.                 self.show_IPP_Error(e, msg)
  6507.                 self.mainapp.cups._end_operation ()
  6508.                 return
  6509.             self.mainapp.cups._end_operation ()
  6510.         elif self.dialog_mode == "device":
  6511.             self.mainapp.cups._begin_operation (_("modifying printer %s") %
  6512.                                                 name)
  6513.             try:
  6514.                 uri = self.getDeviceURI()
  6515.                 if not self.install_hplip_plugin(uri):
  6516.                     self.on_NPCancel(None)
  6517.                     return
  6518.  
  6519.                 self.mainapp.cups.addPrinter(name, device=uri)
  6520.             except cups.IPPError, (e, msg):
  6521.                 self.show_IPP_Error(e, msg)
  6522.                 self.mainapp.cups._end_operation ()
  6523.                 return
  6524.             self.mainapp.cups._end_operation ()
  6525.         elif self.dialog_mode == "ppd":
  6526.             if not ppd:
  6527.                 ppd = self.ppd = self.getNPPPD()
  6528.                 if not ppd:
  6529.                     # Go back to previous page to re-select driver.
  6530.                     self.nextNPTab(-1)
  6531.                     return
  6532.  
  6533.             self.mainapp.cups._begin_operation (_("modifying printer %s") %
  6534.                                                 name)
  6535.             # set ppd on server and retrieve it
  6536.             # cups doesn't offer a way to just download a ppd ;(=
  6537.             raw = False
  6538.             if isinstance(ppd, str) or isinstance(ppd, unicode):
  6539.                 if self.rbtnChangePPDasIs.get_active():
  6540.                     # To use the PPD as-is we need to prevent CUPS copying
  6541.                     # the old options over.  Do this by setting it to a
  6542.                     # raw queue (no PPD) first.
  6543.                     try:
  6544.                         self.mainapp.cups.addPrinter(name, ppdname='raw')
  6545.                     except cups.IPPError, (e, msg):
  6546.                         self.show_IPP_Error(e, msg)
  6547.                 try:
  6548.                     self.mainapp.cups.addPrinter(name, ppdname=ppd)
  6549.                 except cups.IPPError, (e, msg):
  6550.                     self.show_IPP_Error(e, msg)
  6551.                     self.mainapp.cups._end_operation ()
  6552.                     return
  6553.  
  6554.                 try:
  6555.                     filename = self.mainapp.cups.getPPD(name)
  6556.                     ppd = cups.PPD(filename)
  6557.                     os.unlink(filename)
  6558.                 except cups.IPPError, (e, msg):
  6559.                     if e == cups.IPP_NOT_FOUND:
  6560.                         raw = True
  6561.                     else:
  6562.                         self.show_IPP_Error(e, msg)
  6563.                         self.mainapp.cups._end_operation ()
  6564.                         return
  6565.             else:
  6566.                 # We have an actual PPD to upload, not just a name.
  6567.                 if ((not self.rbtnChangePPDasIs.get_active()) and
  6568.                     isinstance (self.mainapp.ppd, cups.PPD)):
  6569.                     cupshelpers.copyPPDOptions(self.mainapp.ppd, ppd)
  6570.                 else:
  6571.                     # write Installable Options to ppd
  6572.                     for option in self.options.itervalues():
  6573.                         option.writeback()
  6574.                     cupshelpers.setPPDPageSize(ppd, self.language[0])
  6575.  
  6576.                 try:
  6577.                     self.mainapp.cups.addPrinter(name, ppd=ppd)
  6578.                 except cups.IPPError, (e, msg):
  6579.                     self.show_IPP_Error(e, msg)
  6580.  
  6581.             self.mainapp.cups._end_operation ()
  6582.  
  6583.             if not raw:
  6584.                 check = True
  6585.                 checkppd = ppd
  6586.  
  6587.         self.NewPrinterWindow.hide()
  6588.         self.mainapp.populateList()
  6589.         self.mainapp.fillPrinterTab (name)
  6590.         if check:
  6591.             try:
  6592.                 self.checkDriverExists (name, ppd=checkppd)
  6593.             except:
  6594.                 nonfatalException()
  6595.  
  6596.             # Also check to see whether the media option has become
  6597.             # invalid.  This can happen if it had previously been
  6598.             # explicitly set to a page size that is not offered with
  6599.             # the new PPD (see bug #441836).
  6600.             try:
  6601.                 option = self.mainapp.server_side_options['media']
  6602.                 if option.get_current_value () == None:
  6603.                     debugprint ("Invalid media option: resetting")
  6604.                     option.reset ()
  6605.                     self.mainapp.changed.add (option)
  6606.                     self.mainapp.save_printer (self.mainapp.printer)
  6607.             except KeyError:
  6608.                 pass
  6609.             except:
  6610.                 nonfatalException()
  6611.  
  6612.         # Finally, suggest printing a test page.
  6613.         if (self.dialog_mode == "printer" or \
  6614.             self.dialog_mode == "printer_with_uri") and \
  6615.             self.mainapp.ppd != False:
  6616.             q = gtk.MessageDialog (self.mainapp.PrintersWindow,
  6617.                                    gtk.DIALOG_DESTROY_WITH_PARENT |
  6618.                                    gtk.DIALOG_MODAL,
  6619.                                    gtk.MESSAGE_QUESTION,
  6620.                                    gtk.BUTTONS_YES_NO,
  6621.                                    _("Would you like to print a test page?"))
  6622.             response = q.run ()
  6623.             q.destroy ()
  6624.             if response == gtk.RESPONSE_YES:
  6625.                 # Display the properties dialog.
  6626.                 self.mainapp.display_properties_dialog_for (name)
  6627.  
  6628.                 # Click the test button.
  6629.                 self.mainapp.btnPrintTestPage.clicked ()
  6630.  
  6631.     def checkDriverExists(self, name, ppd=None):
  6632.         """Check that the driver for an existing queue actually
  6633.         exists, and prompt to install the appropriate package
  6634.         if not.
  6635.  
  6636.         ppd: cups.PPD object, if already created"""
  6637.  
  6638.         # Is this queue on the local machine?  If not, we can't check
  6639.         # anything at all.
  6640.         server = cups.getServer ()
  6641.         if not (server == 'localhost' or server == '127.0.0.1' or
  6642.                 server == '::1' or server[0] == '/'):
  6643.             return
  6644.  
  6645.         # Fetch the PPD if we haven't already.
  6646.         if not ppd:
  6647.             try:
  6648.                 filename = self.mainapp.cups.getPPD(name)
  6649.             except cups.IPPError, (e, msg):
  6650.                 if e == cups.IPP_NOT_FOUND:
  6651.                     # This is a raw queue.  Nothing to check.
  6652.                     return
  6653.                 else:
  6654.                     self.show_IPP_Error(e, msg)
  6655.                     return
  6656.  
  6657.             ppd = cups.PPD(filename)
  6658.             os.unlink(filename)
  6659.  
  6660.         (pkgs, exes) = cupshelpers.missingPackagesAndExecutables (ppd)
  6661.         if len (pkgs) > 0 or len (exes) > 0:
  6662.             # We didn't find a necessary executable.  Complain.
  6663.             can_install = False
  6664.             if len (pkgs) > 0:
  6665.                 try:
  6666.                     pk = installpackage.PackageKit ()
  6667.                     can_install = True
  6668.                 except:
  6669.                     pass
  6670.  
  6671.             if can_install and len (pkgs) > 0:
  6672.                 pkg = pkgs[0]
  6673.                 install_text = ('<span weight="bold" size="larger">' +
  6674.                                 _('Install driver') + '</span>\n\n' +
  6675.                                 _("Printer '%s' requires the %s package but "
  6676.                                   "it is not currently installed.") %
  6677.                                 (name, pkg))
  6678.                 dialog = self.InstallDialog
  6679.                 self.lblInstall.set_markup(install_text)
  6680.                 dialog.set_transient_for (self.mainapp.PrintersWindow)
  6681.                 response = dialog.run ()
  6682.                 dialog.hide ()
  6683.                 if response == gtk.RESPONSE_OK:
  6684.                     # Install the package.
  6685.                     try:
  6686.                         xid = self.mainapp.PrintersWindow.window.xid
  6687.                         pk.InstallPackageName (xid, 0, pkg)
  6688.                     except:
  6689.                         pass # should handle error
  6690.             else:
  6691.                 show_error_dialog (_('Missing driver'),
  6692.                                    _("Printer '%s' requires the '%s' program "
  6693.                                      "but it is not currently installed.  "
  6694.                                      "Please install it before using this "
  6695.                                      "printer.") % (name, (exes + pkgs)[0]),
  6696.                                    self.mainapp.PrintersWindow)
  6697.  
  6698.  
  6699. def main(setup_printer = None, configure_printer = None, change_ppd = False,
  6700.          devid = "", print_test_page = False, focus_on_map = True):
  6701.     cups.setUser (os.environ.get ("CUPS_USER", cups.getUser()))
  6702.     gtk.gdk.threads_init()
  6703.  
  6704.     mainwindow = GUI(setup_printer, configure_printer, change_ppd, devid,
  6705.                      print_test_page, focus_on_map)
  6706.  
  6707.     if gtk.__dict__.has_key("main"):
  6708.         gtk.main()
  6709.     else:
  6710.         gtk.mainloop()
  6711.  
  6712.  
  6713. if __name__ == "__main__":
  6714.     import getopt
  6715.     try:
  6716.         opts, args = getopt.gnu_getopt (sys.argv[1:], '',
  6717.                                         ['setup-printer=',
  6718.                                          'configure-printer=',
  6719.                                          'choose-driver=',
  6720.                                          'devid=',
  6721.                                          'print-test-page=',
  6722.                                          'no-focus-on-map',
  6723.                                          'debug'])
  6724.     except getopt.GetoptError:
  6725.         show_help ()
  6726.         sys.exit (1)
  6727.  
  6728.     setup_printer = None
  6729.     configure_printer = None
  6730.     change_ppd = False
  6731.     print_test_page = False
  6732.     focus_on_map = True
  6733.     devid = ""
  6734.     for opt, optarg in opts:
  6735.         if (opt == "--configure-printer" or
  6736.             opt == "--choose-driver" or
  6737.             opt == "--print-test-page"):
  6738.             configure_printer = optarg
  6739.             if opt == "--choose-driver":
  6740.                 change_ppd = True
  6741.             elif opt == "--print-test-page":
  6742.                 print_test_page = True
  6743.  
  6744.         elif opt == '--setup-printer':
  6745.             setup_printer = optarg
  6746.  
  6747.         elif opt == '--devid':
  6748.             devid = optarg
  6749.  
  6750.         elif opt == '--no-focus-on-map':
  6751.             focus_on_map = False
  6752.  
  6753.         elif opt == '--debug':
  6754.             set_debugging (True)
  6755.  
  6756.     main(setup_printer, configure_printer, change_ppd, devid, print_test_page,
  6757.          focus_on_map)
  6758.